:root {
    --bg: #f3f3f9;          /* a breath of the house purple; muted text 4.84:1 on it */
    --surface: #ffffff;
    --border: #e3e3e8;
    --border-soft: #ececf0;
    --text: #1c1c22;
    /* Nudged one step darker than it was: at #767680 the secondary text came
       to 4.49:1 on white and 3.98:1 on the grey page ground — just under the
       4.5 needed to be comfortably readable. This clears it on both. */
    --muted: #6a6a74;
    --primary: #4f46e5;
    --primary-hover: #4338ca;
    --primary-soft: #eef0fe;

    --green: #27a567;
    /* Deeper versions of the two signal colours, for where they have to be
       *read* rather than just seen: text on white, or a ground under white
       text. The flat ones are pitched for blocks of colour, not letters. */
    --green-deep: #1d7a4c;
    --red-deep: #b8392a;
    --red: #e0523f;
    --orange: #e8721c;
    --blue: #2f80ed;
    --navy: #24608f;

    /* The top bar's height. Everything that sits under it — the sticky
       sidebar, the chat column that fills the window — measures from here.
       It used to be written as «49px» in five places, and when captions were
       added under the bar's controls the bar grew and all five became wrong:
       the sidebar tucked under the header and the page gained a scrollbar it
       did not need. */
    --topbar-h: 64px;

    /* Two gaps, used everywhere: between rows of one list, and between the
       blocks of a page. Written once so a page cannot drift to 8px here and
       20px there — measured on 2026-09-16, it had, from 0 to 20. */
    --gap-list: 0.75rem;    /* 12px */
    --gap-block: 1.25rem;   /* 20px */

    --radius-lg: 16px;
    --radius: 12px;
    --radius-sm: 9px;
    /* Two shadows rather than a border. A hairline border and a shadow doing
       the same job at once is what made every card look drawn-on; the light
       alone lifts it off the page and leaves the edge soft. */
    --shadow: 0 1px 2px rgba(28, 25, 60, 0.04), 0 8px 24px rgba(28, 25, 60, 0.06);
    --shadow-lift: 0 2px 6px rgba(28, 25, 60, 0.06), 0 14px 34px rgba(28, 25, 60, 0.09);

    /* The house colour. One colour for every section: the portal used to
       re-tint itself per area, which made the same page furniture look like
       four different products.

       Three variables rather than one, because the accent is a strong colour
       and can only do one of the two jobs a brand colour is asked for: it
       carries text on top of it, and it is written in on a pale ground.

       Text sitting ON this purple is always white, and that is measured
       rather than preferred: white on it is 6.29:1, comfortably clear of the
       4.5 ordinary text needs. (The blue this replaced sat at 4.70:1 — it
       passed, but only just; the purple has room to spare.) So every header,
       bar and filled row carries white.

       Writing IN the colour on a light ground is the other job, and the ink
       is a step deeper for it: 7.90:1 on white and 6.79:1 on the pale tint,
       so a small label in the house colour is as readable as body text. The
       deepest shade exists only to mark the edge of a row already filled with
       the accent — 1.63:1 against it, enough to see, not enough to shout. */
    --accent: #4f46e5;          /* headers, bars, the active row */
    --accent-soft: #eeecfd;     /* pale ground behind an active row */
    --accent-ink: #4338ca;      /* the colour as *text*, on a light ground */
    --accent-deep: #352c99;     /* the edge of a row already filled with it */
    --on-accent: #ffffff;       /* text sitting on --accent */
}


* { box-sizing: border-box; }

/* «hidden» means hidden, whatever else the element is told.
   The browser's own rule for it is the weakest rule on the page, so any
   component that sets «display» — a flex menu, a grid panel — quietly
   overrides it and stays on screen. Measured on the «@» menu: hidden=true,
   display:flex, an empty 224×10 shadow sitting under the report box. */
[hidden] { display: none !important; }

body {
    margin: 0;
    /* Yekan Bakh is the organisation's typeface; Vazirmatn stays as the
       fallback so text still renders correctly if a face fails to load. */
    font-family: "Yekan Bakh", "Vazirmatn", Tahoma, "Segoe UI", Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    /* «overflow-x: hidden» here was not enough, and the way it failed is worth
       writing down. Overflow set on the body is handed up to the viewport, so
       the viewport becomes the scroller — and in a right-to-left page the
       overflow is on the *start* side, which parks that scroller at
       scrollX = -40. Measured on تقویم وظایف: innerWidth 415 against a 375px
       screen, the whole page shifted 40px and its right edge cut off.

       «clip» does not make a scroller at all, so there is no scroll position
       left to be wrong. The hidden line stays underneath it for browsers that
       do not know «clip» yet. Anything genuinely wide — a table — scrolls
       inside its own box, not the page's. */
    overflow-x: hidden;
}

html {
    overflow-x: hidden;
    overflow-x: clip;
}

a { color: inherit; }
h1 { font-size: 1.35rem; margin: 0 0 0.35rem; }
h2 { font-size: 1.05rem; margin: 1.5rem 0 0.8rem; }
.muted { color: var(--muted); }

/* ---------- Top bar (re-tints per section, like the reference) ---------- */
.topbar {
    background: var(--accent);
    color: var(--on-accent);
    display: flex;
    align-items: center;
    gap: 1rem;
    /* Fixed rather than grown from its contents, so the variable everything
       else measures against is the truth and not an estimate. */
    height: var(--topbar-h);
    padding: 0 1.25rem;
    position: sticky;
    top: 0;
    z-index: 40;
}
.topbar-toggle {
    display: none;
    background: none; border: none; color: var(--on-accent);
    font-size: 1.25rem; cursor: pointer; padding: 0;
}
.brand { display: flex; align-items: center; gap: 0.55rem; font-weight: 700; flex-shrink: 0; }
.brand-mark {
    /* The mark is a line drawing and carries itself; the tinted square it used
       to sit in was scaffolding for the single letter that was there before. */
    width: auto; height: 38px;
    object-fit: contain;
    flex-shrink: 0;
}
/* On the blue bar the mark is white. The file is a dark line drawing — right
   for the login page's white ground, unreadable against the bar — and this
   paints it in the bar's own text colour rather than keeping two files of the
   same drawing in step. */
.topbar .brand-mark { filter: brightness(0) invert(1); }
.brand-name { font-size: 1rem; font-weight: 800; white-space: nowrap; }
.workspace-name {
    font-size: 0.85rem;
    /* اسم شرکت کنار اسم سامانه کم‌رنگ‌تر از آن بود که خوانده شود؛ کمی وزن و
       کمی روشنایی بیشتر، بی‌آنکه با «کارینا کُر» رقابت کند. */
    font-weight: 700;
    opacity: 1;
    padding-inline-start: 0.75rem;
    border-inline-start: 1px solid rgba(0, 0, 0, 0.22);
    white-space: nowrap;
}
.topbar-search { flex: 1; display: flex; justify-content: center; min-width: 0; }
.search-wrap {
    position: relative;
    width: 100%;
    max-width: 340px;
    display: flex;
    align-items: center;
}
/* The selector carries «form» and the attribute on purpose.
   The search box lives inside a <form>, so the portal-wide rule for form
   controls («form input:not([type=checkbox])…», further down this file) has
   the heavier hand and was quietly winning: the 2.2rem it is given here to
   keep clear of the magnifier came out as 0.85rem, and the placeholder sat
   right on top of the icon. Measured at 1920px: text began 13.6px from the
   edge, the icon spanned 5–34px. This selector outweighs that one. */
.topbar form.topbar-search input[type="search"] {
    width: 100%;
    min-height: 2.2rem;
    padding: 0.45rem 0.9rem;
    /* room for the magnifier, plus a breath */
    padding-inline-start: 2.45rem;
    border: none;
    border-radius: 999px;
    background: #fff;
    color: var(--text);
    font-family: inherit;
    font-size: 0.88rem;
    box-shadow: none;
}
.topbar form.topbar-search input[type="search"]::placeholder { color: #8a8a94; }
.topbar form.topbar-search input[type="search"]:focus {
    outline: none;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.55);
}
/* The browser's own clear-cross would sit under our results panel. */
.topbar form.topbar-search input[type="search"]::-webkit-search-cancel-button { display: none; }

/* A label now, not a button — the box searches on its own. Black, because a
   white mark on a white pill is not a mark. */
.search-mark {
    position: absolute;
    inset-inline-start: 0.7rem;
    display: flex;
    align-items: center;
    color: #1c1c22;
    pointer-events: none;
    line-height: 1;
}

/* ── what it finds, under the box ─────────────────────────────── */
.quick-results {
    position: absolute;
    top: calc(100% + 0.4rem);
    inset-inline: 0;
    z-index: 60;
    max-height: 70vh;
    overflow-y: auto;
    background: var(--panel, #fff);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
    padding: 0.35rem;
    text-align: start;
}
.quick-group + .quick-group { border-top: 1px solid var(--border); margin-top: 0.35rem; padding-top: 0.35rem; }
.quick-kind {
    font-size: 0.72rem;
    color: var(--muted);
    padding: 0.3rem 0.6rem 0.2rem;
}
.quick-item {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.45rem 0.6rem;
    border-radius: 8px;
    color: var(--text);
    text-decoration: none;
}
.quick-item:hover, .quick-item:focus { background: var(--accent-soft); }
.quick-title {
    font-size: 0.85rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.quick-sub { font-size: 0.72rem; color: var(--muted); }
.quick-empty { padding: 0.8rem 0.6rem; font-size: 0.83rem; color: var(--muted); margin: 0; }
.quick-all {
    display: block;
    padding: 0.5rem 0.6rem;
    margin-top: 0.2rem;
    border-top: 1px solid var(--border);
    font-size: 0.8rem;
    color: var(--accent-ink);
    text-decoration: none;
}
.quick-all:hover { text-decoration: underline; }
.topbar-actions { display: flex; align-items: center; gap: 0.75rem; flex-shrink: 0; }
.topbar-avatar, .topbar-disc {
    /* One size and one shape for the three of them. They are the same kind of
       thing — a control at the end of the bar — and three different sizes
       made the row look assembled rather than designed. */
    width: 30px; height: 30px;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.15);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    text-decoration: none;
    color: var(--on-accent);
}
/* Only the avatar clips: the bell's count deliberately sits outside its
   circle, and clipping it would cut the number in half. */
.topbar-avatar { overflow: hidden; font-weight: 700; font-size: 0.72rem; }
.topbar-avatar img { width: 100%; height: 100%; object-fit: cover; }
/* The dimming belongs to the icon, not to the whole control: applied to the
   link it also faded the caption, and a 12px word at 90% on a mid-tone ground
   is exactly the kind of text people squint at. */
.topbar-disc, .topbar-avatar { opacity: 0.9; }
.topbar-item:hover .topbar-disc,
.topbar-item:hover .topbar-avatar { opacity: 1; }

/* The circle keeps its size; the caption sits under it, so the row's rhythm
   is unchanged and only the bar gets a little taller. */
.topbar-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Enough air that the word reads as a caption under the icon rather than
       as something stuck to it. */
    gap: 5px;
    text-decoration: none;
    color: var(--on-accent);
    flex-shrink: 0;
}
/* 0.62rem worked out at under 10px, which is below where Persian type stays
   legible at a glance — the descenders and the dots run together. */
.topbar-cap { font-size: 0.75rem; line-height: 1.2; white-space: nowrap; }

/* ---------- Layout: fixed right sidebar + content ---------- */
.layout { display: flex; align-items: flex-start; }

.sidebar {
    width: 250px;
    flex-shrink: 0;
    background: var(--surface);
    border-inline-start: 0;
    height: calc(100vh - var(--topbar-h));
    position: sticky;
    top: var(--topbar-h);
    /* The rail and its button reach past the sidebar's edge into the page, so
       the box itself does not clip; the list inside it scrolls instead. The
       z-index keeps the button above cards that are positioned themselves. */
    overflow: visible;
    z-index: 5;
    transition: width 0.22s ease;
}
/* ── the list ──
   As in the reference: a quiet column where the only colour is the icon of
   the page you are on. The row itself never fills — the label stays dark and
   simply gets heavier — so the eye finds the one purple square and nothing
   competes with it. */
.side-nav {
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 1rem 0.85rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    scrollbar-width: thin;
}
.side-link {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.4rem 0.5rem;
    border-radius: var(--radius);
    text-decoration: none;
    color: #52525c;                 /* 7.72:1 on white */
    font-size: 0.9rem;
    font-weight: 500;
    background: none;
    white-space: nowrap;
    transition: color 0.14s ease;
}
.side-link:hover { color: var(--text); }
.side-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.side-link.active {
    color: var(--text);
    font-weight: 700;
    background: none;
    box-shadow: none;
}
/* Every icon sits in the same square, filled or not, so the labels start on
   one line whether a row is active or not. */
.side-icon {
    flex: 0 0 2.35rem;
    width: 2.35rem;
    height: 2.35rem;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #6a6a74;                 /* 5.35:1 — a mark needs 3 */
    background: transparent;
    transition: background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease;
}
.side-link:hover .side-icon { background: var(--accent-soft); color: var(--accent-ink); }
.side-link.active .side-icon {
    background: var(--accent);
    color: #fff;                    /* 6.29:1 */
    box-shadow: 0 6px 14px rgba(79, 70, 229, 0.3);
}
.side-label { flex: 1; }
.side-badges { display: flex; gap: 0.3rem; }
.side-badge {
    min-width: 20px;
    padding: 0.05rem 0.4rem;
    border-radius: 5px;
    font-size: 0.72rem;
    font-weight: 700;
    color: #fff;
    text-align: center;
}
/* A count in the sidebar is a solid pill, not the pale chip «badge-green» and
   its family draw elsewhere — so it carries its own text colour as well as its
   ground. Setting only the background let the pale rule's dark lettering win
   the cascade, which put dark blue text on a blue pill. */
.side-badge.badge-green { background: var(--green); color: #fff; }
.side-badge.badge-red { background: var(--red); color: #fff; }
.side-badge.badge-orange { background: var(--orange); color: #fff; }
.side-badge.badge-blue { background: var(--blue); color: #fff; }

.side-panel { border-top: 1px solid var(--border-soft); }
.side-panel-head {
    background: var(--surface);
    color: var(--text);
    border-bottom: 1px solid var(--border-soft);
    padding: 0.6rem 1.1rem;
    font-size: 0.88rem;
    font-weight: 800;
}
/* The whole company is in here now, so it scrolls rather than pushing the
   column past the end of the page. */
.side-panel-body {
    padding: 0.4rem 0;
    max-height: 22rem;
    overflow-y: auto;
    scrollbar-width: thin;
}
.colleague-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 1.1rem;
}
.colleague-item:hover { background: var(--bg); }
.colleague-avatar {
    width: 32px; height: 32px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}
.colleague-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft);
    color: var(--accent-ink);
    font-size: 0.8rem; font-weight: 700;
}
.colleague-text { min-width: 0; }
/* A Latin name in an RTL column lays itself out left-to-right, so a long one
   is cut at its far end rather than at its first letter: «Amirhossein R…»
   tells you who it is, «…rhossein Rahimi» does not. */
.colleague-name {
    font-size: 0.84rem; font-weight: 500;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    unicode-bidi: plaintext;
}
.colleague-sub { font-size: 0.72rem; color: var(--muted); }

/* No cap on the working area.
   There used to be a 1100px ceiling here, on the reasoning that a long line of
   text is tiring to read. That reasoning is right for a page of prose and
   wrong for this portal: almost every screen is a table, a board or a row of
   cards, and on a 1920px monitor the cap left 580 pixels of empty white down
   the left-hand side while the table beside it squeezed its columns. Measured,
   not guessed.
   The few places that really are prose carry their own measure —
   «.letter-body-box» stops at 720px, the sign-in card at 380px — so widening
   the frame does not stretch any of them. */
.content { flex: 1; min-width: 0; padding: 1.5rem; }
.content.content-wide { max-width: none; }
.container { max-width: 1100px; margin: 0 auto; padding: 2rem 1.5rem; }

@media (max-width: 900px) {
    .topbar-toggle { display: block; }
    .workspace-name { display: none; }
    .sidebar {
        position: fixed;
        top: var(--topbar-h); right: 0;
        height: calc(100vh - var(--topbar-h));
        z-index: 30;
        transform: translateX(100%);
        transition: transform 0.2s;
        box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
    }
    body.sidebar-open .sidebar { transform: translateX(0); }
    .content { padding: 1rem; }
    .brand-name { display: none; }
}

/* ---------- Page head + tabs ---------- */
.page-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 0.75rem;
}
.page-header { /* legacy alias */
    display: flex; justify-content: space-between; align-items: center;
    gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.page-header h1, .page-head h1 { margin: 0; }

/* A heading whose sentence belongs under it rather than beside it: read
   across, a title and its explanation ran together as one long line. */
.page-head-stacked { flex-direction: column; align-items: stretch; gap: 0.2rem; }
.page-head-stacked .page-sub { margin: 0; font-size: 0.88rem; }

/* A figure, and the work it counted. Every one of these is a link: «تمام
   کارت‌هایی که مقداری یا کاری رو نشون میدن قابلیت کلیک کردن داشته باشن». */
/* Six figures, and column counts that divide six: auto-fill kept leaving
   five on one line and a single card stranded on the next, which is exactly
   the raggedness the row is meant to avoid. */
.mon-cards {
    display: grid; gap: 0.7rem; margin-bottom: 1.1rem;
    grid-template-columns: repeat(6, 1fr);
}
@media (max-width: 1200px) { .mon-cards { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px)  { .mon-cards { grid-template-columns: repeat(2, 1fr); } }
.mon-card {
    display: flex; flex-direction: column; gap: 0.15rem;
    padding: 0.85rem 1rem;
    background: var(--surface); border: 1px solid var(--border-soft);
    border-radius: var(--radius); box-shadow: var(--shadow);
    color: var(--text); text-decoration: none;
}
a.mon-card:hover { border-color: var(--accent); background: var(--accent-soft); }
.mon-card-num { font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
.mon-card-cap { font-size: 0.8rem; color: var(--muted); }
.mon-card.is-warn .mon-card-num { color: var(--red-deep); }

/* The mark a tab wears, before its word. It takes the tab's own colour, so
   an active tab's icon goes with its text rather than staying grey. */
.tab-icon {
    display: inline-flex; align-items: center;
    margin-inline-end: 0.35rem; vertical-align: -0.2em;
}
.tab-icon .icn { display: block; }

.tabs {
    display: flex;
    gap: 0.15rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--border-soft);
    overflow-x: auto;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
    text-decoration: none;
    color: var(--muted);
    font-size: 0.9rem;
    font-weight: 500;
    padding: 0.6rem 0.95rem;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    white-space: nowrap;
}
.tab:hover { color: var(--text); }
/* The active tab keeps the weight of the others. Bold is wider than regular,
   so a row of tabs re-flowed every time a different one became active — the
   labels shifted sideways as the new page arrived, which is the «jump» being
   reported. The colour and the underline say which one is open, and they say
   it without moving anything. */
.tab.active { color: var(--accent-ink); border-bottom-color: var(--accent-ink); }

/* ---------- Buttons & forms ---------- */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent);
    color: var(--on-accent);
    border: none;
    padding: 0.6rem 1.25rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
    font-family: inherit;
    transition: filter 0.15s;
}
.btn:hover { filter: brightness(0.92); }
.btn-block { width: 100%; margin-top: 1.25rem; }

form label {
    display: block;
    margin: 0.9rem 0 0.35rem;
    font-size: 0.85rem;
    font-weight: 500;
}
form label:first-of-type { margin-top: 0; }
form input:not([type="checkbox"]):not([type="radio"]),
form select,
form textarea {
    width: 100%;
    /* One height for every control in the portal. A select carries the
       browser's own padding and a picker is a button, so each was a
       different height beside the next; naming the height once is what makes
       a row of them line up. */
    min-height: 2.6rem;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: inherit;
    font-size: 0.9rem;
    background: var(--bg);
    color: var(--text);
    transition: border-color 0.15s, background 0.15s;
}
form input::placeholder, form textarea::placeholder { color: #a6a6b0; }
form input:focus, form select:focus, form textarea:focus {
    outline: none; border-color: var(--accent-ink); background: var(--surface);
}
.form-box {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    padding: 1.5rem;
    border-radius: var(--radius-lg);
    max-width: 560px;
    box-shadow: var(--shadow);
    margin-bottom: var(--gap-block);
}
.form-box .btn { margin-top: 1.25rem; }
.inline-form { margin-bottom: 1.25rem; display: flex; align-items: center; gap: 0.6rem; }
.inline-form select { width: auto; }
.error {
    color: #b91c1c; background: #fef2f2; border: 1px solid #fecaca;
    padding: 0.55rem 0.8rem; border-radius: var(--radius-sm);
    font-size: 0.88rem; margin-bottom: 1rem;
}
.date-hint { font-size: 0.8rem; margin-top: 0.3rem; }

/* ---------- Login (no chrome) ---------- */
.login-page {
    min-height: calc(100vh - 4rem);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    gap: 1.75rem; padding: 2rem 1rem;
}
.login-brand { display: flex; align-items: center; gap: 0.85rem; }

.brand-mark-lg { width: auto; height: 96px; }
.login-brand-title { font-weight: 700; font-size: 1.05rem; }
.login-brand-sub { color: var(--muted); font-size: 0.85rem; }
.login-box {
    width: 100%; max-width: 380px;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    padding: 2rem; border-radius: 20px;
    box-shadow: var(--shadow);
}
.login-box h1 { margin-top: 0; }
.login-hint { color: var(--muted); font-size: 0.88rem; margin: 0 0 1.25rem; }

/* راه‌های دیگر ورود — زیر دکمه، کم‌رنگ‌تر از آن، چون راه اصلی همان بالاست. */
.login-ways {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin-top: 1rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--border);
}
.login-ways a {
    font-size: 0.82rem;
    color: var(--accent-ink);
    text-decoration: none;
}
.login-ways a:hover { text-decoration: underline; }

/* کد یک‌بارمصرف: ارقام از هم باز، تا خواندن و مقایسه‌اش با پیامک آسان باشد. */
.otp-input {
    letter-spacing: 0.7em;
    text-indent: 0.7em;
    text-align: center;
    font-size: 1.15rem;
    font-variant-numeric: tabular-nums;
    direction: ltr;
}
/* The sign-in page has no top bar to take its colour from, so it named the
   old indigo directly. It is the first thing anyone sees, and it should be
   the house colour like everything else. */
.no-chrome .btn { background: var(--accent); color: var(--on-accent); }

/* ---------- Panels (coloured headers, as in the reference dashboard) ---------- */
.panel {
    background: var(--surface);
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    /* Clipped so a table and a coloured head keep the card's rounded corners.
       The one thing that legitimately reaches outside a panel is a date
       picker's calendar, which is why it lifts the clipping while it is open
       rather than being drawn cut in half. */
    overflow: hidden;
    margin-bottom: 1.25rem;
}
.panel.has-open-picker { overflow: visible; }
.panel-head {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.85rem 1.1rem;
    font-size: 0.95rem;
    font-weight: 800;
    color: var(--text);
    background: var(--surface);
    /* The rule is the whole separation: title above it, contents below. */
    border-bottom: 1px solid var(--border-soft);
}
/* A short upright bar in the house colour, in place of the band of colour
   the head used to be. It is what a coloured head was for — saying at a
   glance which panel this is — at a hundredth of the ink. */
.panel-head::before {
    content: "";
    flex-shrink: 0;
    inline-size: 4px;
    block-size: 1.05em;
    border-radius: 999px;
    background: var(--accent);
}
.panel-head-green::before { background: var(--green-deep); }
.panel-head-red::before { background: var(--red-deep); }
.panel-head-orange::before { background: var(--orange); }
.panel-head-blue::before { background: var(--accent); }
/* The card's own inset, and the whole of it. Everything dropped into a
   panel body is held off the border by this one rule rather than by being
   named in a list — which is how buttons, charts, forms and pickers kept
   turning up flush against the edge one at a time. */
.panel-body { padding: 0.4rem 1.1rem; }

/* The exceptions run the full width on purpose, because each of them
   already insets its own contents: a row's padding is what lines a list up
   inside its card, and a table does the same with its cells. They pull back
   out over the body's padding rather than the body going without it. */
.panel-body > .line-list,
.panel-body > .line-row,
.panel-body > table,
.panel-body > .table,
.panel-body > .table-wrap,
.panel-body > .sheet-block,
.panel-body > .unit-strip,
.panel-body > .kpi-table { margin-inline: -1.1rem; }


/* A form that is the only thing in the panel needs air under it too. */
.panel-body > form:last-child { margin-block-end: 1rem; }
.empty-note { padding: 1.25rem 0; margin: 0; font-size: 0.88rem; }

.line-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.65rem 1.1rem;
    border-bottom: 1px solid var(--border-soft);
    text-decoration: none;
    color: var(--text);
}
.line-row:last-child { border-bottom: none; }
.line-row:hover { background: var(--bg); }
.line-title { flex: 1; font-size: 0.9rem; min-width: 0; }
.line-date { font-size: 0.78rem; color: var(--muted); white-space: nowrap; }

/* ---------- Dashboard (میزکار) ---------- */
.desk-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; }
.greeting-chip {
    display: inline-block;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: 999px;
    padding: 0.3rem 0.9rem;
    font-size: 0.82rem;
    color: var(--muted);
    margin-top: 0.4rem;
}

.stat-grid { display: flex; gap: 0.9rem; flex-wrap: wrap; margin: 1.25rem 0; }
.stat {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    padding: 1rem 1.4rem;
    display: flex; flex-direction: column; align-items: center; gap: 0.2rem;
    min-width: 118px;
    box-shadow: var(--shadow);
    text-decoration: none;
    color: var(--text);
    transition: transform 0.1s, box-shadow 0.1s;
}
.stat:hover { transform: translateY(-2px); box-shadow: 0 4px 16px rgba(20, 20, 30, 0.1); }
.stat-icon { font-size: 1.1rem; }
.stat-num { font-size: 1.55rem; font-weight: 700; }
.stat span:last-child { font-size: 0.8rem; color: var(--muted); text-align: center; }
.stat-today  { border-top: 3px solid var(--green); }
.stat-today .stat-num { color: var(--green); }
.stat-overdue { border-top: 3px solid var(--red); }
.stat-overdue .stat-num { color: var(--red); }
.stat-followup { border-top: 3px solid var(--blue); }
.stat-followup .stat-num { color: var(--blue); }
.stat-letters { border-top: 3px solid var(--orange); }
.stat-letters .stat-num { color: var(--orange); }

.team-row {
    display: flex; align-items: center; gap: 0.45rem;
    overflow-x: auto; padding: 0.25rem 0 1.25rem;
}
.team-label { font-size: 0.85rem; flex-shrink: 0; margin-inline-end: 0.25rem; }
.team-avatar {
    width: 34px; height: 34px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0;
    border: 2px solid var(--surface);
    box-shadow: 0 0 0 1px var(--border-soft);
}
.team-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.8rem; font-weight: 700;
}

.desk-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
@media (max-width: 900px) { .desk-columns { grid-template-columns: 1fr; } }

.desk-profile {
    display: flex; align-items: center; gap: 0.9rem;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 0.9rem 1.15rem;
}
.desk-profile-avatar {
    width: 56px; height: 56px; border-radius: 14px;
    object-fit: cover; flex-shrink: 0; font-size: 1.3rem;
}
.desk-profile-name { font-weight: 700; font-size: 0.95rem; }
.desk-profile-role { font-size: 0.8rem; color: var(--accent-ink); font-weight: 600; }
.desk-profile-unit { font-size: 0.76rem; }
.online-dot { font-size: 0.75rem; color: var(--green-deep); margin-top: 0.2rem; }
.online-dot::before {
    content: ''; display: inline-block;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--green); margin-inline-end: 0.3rem;
}

/* ---------- Task rows (coloured leading edge, like the reference) ---------- */
.task-list { display: flex; flex-direction: column; gap: var(--gap-list); }
.task-group-title { font-size: 0.85rem; color: var(--muted); margin: 1.25rem 0 0.6rem; font-weight: 600; }
.task-row {
    display: flex; align-items: flex-start; gap: 0.8rem;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-inline-start: 4px solid var(--green);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    box-shadow: var(--shadow);
    text-decoration: none; color: var(--text);
}
.task-row.prio-important { border-inline-start-color: var(--orange); }
.task-row.prio-urgent { border-inline-start-color: var(--red); }
.task-row.task-done { opacity: 0.55; }
.task-row.task-done .task-title { text-decoration: line-through; }
.task-check {
    width: 22px; height: 22px;
    border-radius: 6px;
    border: 1.5px solid var(--border);
    background: var(--bg);
    color: var(--green);
    font-size: 0.8rem; cursor: pointer;
    flex-shrink: 0; margin-top: 0.1rem;
}
.task-body { flex: 1; min-width: 0; }
.task-title {
    font-weight: 600; font-size: 0.92rem;
    text-decoration: none; color: var(--text); display: inline-block;
}
.task-title:hover { color: var(--accent-ink); }
.task-desc { font-size: 0.84rem; margin-top: 0.15rem; }
.task-meta { display: flex; gap: 0.9rem; flex-wrap: wrap; margin-top: 0.35rem; font-size: 0.76rem; color: var(--muted); }
.task-side { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }

/* ---------- Letter rows (cartable) ---------- */
.letter-list { display: flex; flex-direction: column; gap: var(--gap-list); }
.letter-row {
    display: flex; align-items: center; gap: 0.85rem;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 0.8rem 1rem;
    box-shadow: var(--shadow);
    text-decoration: none; color: var(--text);
}
.letter-row:hover { border-color: var(--accent-ink); }
.letter-row.letter-unread { border-inline-start: 4px solid var(--orange); }
.letter-row.letter-unread .letter-subject { font-weight: 700; }
.letter-sender { display: flex; align-items: center; gap: 0.55rem; flex-shrink: 0; width: 150px; }
.letter-sender-avatar {
    width: 34px; height: 34px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0;
}
.letter-sender-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.8rem; font-weight: 700;
}
.letter-sender-name { font-size: 0.84rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.letter-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.letter-subject { font-size: 0.92rem; }
.letter-snippet { font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.letter-side { display: flex; align-items: center; gap: 0.7rem; font-size: 0.78rem; flex-shrink: 0; }
.letter-body-box { white-space: pre-wrap; line-height: 1.9; max-width: 720px; }
@media (max-width: 700px) {
    .letter-sender { width: auto; }
    .letter-sender-name { display: none; }
}

/* ---------- Chips & badges ---------- */
.priority { padding: 0.18rem 0.6rem; border-radius: 999px; font-size: 0.74rem; font-weight: 600; white-space: nowrap; }
.priority-normal, .priority-low { background: #e9e9ee; color: #4b5563; }
.priority-important, .priority-high { background: #fdefe2; color: #9a3412; }
.priority-urgent { background: #fdecea; color: #991b1b; }

.status { padding: 0.18rem 0.6rem; border-radius: 999px; font-size: 0.76rem; font-weight: 600; }
.status-open { background: #fef3c7; color: #92400e; }
.status-in_progress { background: #dbeafe; color: #1e40af; }
.status-resolved { background: #dcfce7; color: #166534; }
.status-closed { background: #e5e7eb; color: #374151; }

.tag-list {
    display: flex; gap: 0.35rem; flex-wrap: wrap; align-items: center;
    margin-top: 0.45rem;
}
.tag-chip { padding: 0.12rem 0.55rem; border-radius: 999px; font-size: 0.72rem; font-weight: 600; }
.tag-color-0 { background: #dbeafe; color: #1e40af; }
.tag-color-1 { background: #fce7f3; color: #9d174d; }
.tag-color-2 { background: #dcfce7; color: #166534; }
.tag-color-3 { background: #fef9c3; color: #854d0e; }
.tag-color-4 { background: #ede9fe; color: #5b21b6; }
.tag-color-5 { background: #ffedd5; color: #9a3412; }

.badge {
    /* Same box as a chip, so a badge and a chip standing next to each other
       sit on one line rather than one riding above the other.

       And no bottom margin: a badge is an inline mark that sits *in* a line
       of text or in a row of other marks, and a margin under it lifts it off
       that line — which is what put «ابلاغیه» above its own subject and
       «پاراف ۱» above the words beside it. The room under a badge that stands
       alone over a paragraph belongs to that paragraph, or to `.badge-block`
       for the few places that want it. */
    display: inline-flex; align-items: center; justify-content: center;
    align-self: center; min-height: 1.55rem; line-height: 1.2;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.72rem; font-weight: 600;
    padding: 0.18rem 0.6rem; border-radius: 999px; margin-bottom: 0;
    vertical-align: middle;
}
/* A badge deliberately set on a line of its own, with the text under it. */
.badge-block { margin-bottom: 0.7rem; }
/* A badge standing in a row of marks carries no bottom margin: with the row
   centred, that margin lifted the badge above the chips beside it. The margin
   is for a badge that sits alone above a paragraph. */

.role-badge {
    background: var(--bg); border: 1px solid var(--border-soft); color: var(--muted);
    padding: 0.2rem 0.65rem; border-radius: 999px; font-size: 0.78rem;
}

/* ---------- Cards ---------- */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem; }
.card {
    background: var(--surface);
    border: 0;
    padding: 1.35rem;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    display: flex; flex-direction: column;
    transition: box-shadow 0.18s ease, transform 0.18s ease;
}
/* A card that leads somewhere says so by lifting, not by drawing a ring. */
a.card:hover, .card-link:hover { box-shadow: var(--shadow-lift); transform: translateY(-1px); }
.card h3 { margin: 0 0 0.35rem; font-size: 0.98rem; }
.card p { flex: 1; font-size: 0.86rem; }
.card-actions { margin-top: 0.7rem; }
.card-actions .btn { padding: 0.45rem 1rem; font-size: 0.84rem; }
.group-card { text-align: right; align-items: flex-start; text-decoration: none; color: var(--text); }
.group-card:hover { border-color: var(--accent-ink); }
.member-card { flex-direction: row; align-items: center; gap: 0.75rem; }

/* ---------- Tables ---------- */
.table-wrap {
    background: var(--surface);
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow-x: auto;
}
.table { width: 100%; min-width: 620px; border-collapse: collapse; }
/* The header row is a label, not a band. It used to sit on the page grey,
   which made every table look like it started with a filled strip; on white
   with a little letter-spacing it reads as what it is and the eye goes
   straight to the first row of data. */
.table th {
    text-align: right; font-size: 0.75rem; color: var(--muted); font-weight: 600;
    letter-spacing: 0.02em;
    padding: 0.85rem 1.1rem; background: var(--surface);
    border-bottom: 1px solid var(--border-soft);
    /* A heading is two or three words; broken over two lines it stops lining
       up with the column under it and the whole row grows to match. */
    white-space: nowrap;
}
.table td {
    padding: 0.85rem 1.1rem; font-size: 0.88rem;
    /* Lighter than the header's rule: rows are separated, not boxed. */
    border-bottom: 1px solid var(--border-soft);
    white-space: nowrap;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr { transition: background 0.12s ease; }
.table tbody tr:hover td { background: var(--accent-soft); }
/* The exception: a cell that is genuinely prose — a subject line, a note —
   asks to wrap, because the alternative is a column the width of a page. */
.table td.wrap, .table th.wrap { white-space: normal; }

/* A heading belongs over the thing it names. Right-aligned headings above
   cells that start where their content does left the two drifting apart by
   as much as 45px in a single row. Centred, each title is over its column. */
.table th, .table td { text-align: center; }
/* Two that stay at the edge. The first column is the row's own name, and a
   list is read down that edge; and a cell holding a sentence reads as prose,
   which is never centred. */
.table th:first-child, .table td:first-child,
.table th.wrap, .table td.wrap { text-align: start; }
.table tbody tr:hover { background: var(--bg); }
.table tbody tr:last-child td { border-bottom: none; }
.table-avatar { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; }
.table-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.8rem; font-weight: 700;
}

/* ---------- Pickers ---------- */
.group-select { margin-bottom: var(--gap-list); }
.picker-box {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
    max-height: 220px; overflow-y: auto;
    padding: 0.4rem;
    display: flex; flex-direction: column; gap: 0.1rem;
}
.picker-item {
    display: flex; align-items: center; gap: 0.55rem;
    padding: 0.45rem 0.6rem; border-radius: var(--radius-sm);
    cursor: pointer; font-weight: 400; margin: 0;
}
.picker-item:hover { background: var(--surface); }
/* «width: auto» here was from before the size was named once for all of
   them; it left these 13px wide against a 17px height, which is the same
   deformed box in a smaller way. The rule above now covers it. */
.picker-item input[type="checkbox"] { accent-color: var(--accent-ink); }
.picker-avatar { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.picker-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.75rem; font-weight: 700;
}
.picker-name { font-size: 0.87rem; }
.picker-role { font-size: 0.75rem; margin-inline-start: auto; }

/* ---------- Profile ---------- */
.profile-box {
    display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    /* The width of the cards below it: this is the same column, and a narrow
       card above wide ones reads as an unfinished page. */
    padding: 1.5rem; margin-bottom: 1.5rem;
}
.profile-avatar { width: 72px; height: 72px; border-radius: 18px; font-size: 1.6rem; object-fit: cover; }
.unit-avatar-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink); font-weight: 700;
}
.profile-name { font-size: 1.1rem; font-weight: 700; margin-bottom: 0.15rem; }

/* ---------- Units ---------- */
.unit-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.unit-avatar-lg { width: 56px; height: 56px; border-radius: 14px; object-fit: cover; flex-shrink: 0; }
.unit-card { align-items: center; text-align: center; gap: 0.5rem; }
.unit-card .unit-avatar-lg { width: 64px; height: 64px; margin-bottom: 0.25rem; }
.unit-avatar-form { display: flex; flex-direction: column; gap: 0.5rem; width: 100%; margin-top: 0.5rem; }
.unit-avatar-form input[type="file"] { font-size: 0.78rem; }
.unit-avatar-form .btn { padding: 0.45rem 1rem; font-size: 0.82rem; }

/* ---------- Ticket / letter detail ---------- */
.ticket-meta {
    display: flex; gap: 0.5rem 1.15rem; flex-wrap: wrap;
    margin: 0.5rem 0 1.25rem; color: var(--muted); font-size: 0.85rem;
    align-items: center;
}
.comments { margin-bottom: 1.25rem; display: flex; flex-direction: column; gap: 0.55rem; }
.comment {
    background: var(--surface); border: 1px solid var(--border-soft);
    padding: 0.8rem 1.05rem; border-radius: var(--radius);
}
.comment-author { font-weight: 600; font-size: 0.87rem; }
.comment-body { margin-top: 0.2rem; font-size: 0.89rem; }
.comment-date { font-size: 0.74rem; margin-top: 0.35rem; }

.attachment-list { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1.5rem; }
.attachment-item {
    display: inline-flex; align-items: center; gap: 0.4rem; width: fit-content;
    background: var(--surface); border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm); padding: 0.5rem 0.9rem;
    font-size: 0.85rem; text-decoration: none; color: var(--text);
}
.attachment-item:hover { border-color: var(--accent-ink); color: var(--accent-ink); }

/* ---------- Calendar ---------- */
.calendar-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
.calendar-title { font-weight: 700; font-size: 1.02rem; }
.btn-icon {
    text-decoration: none; color: var(--accent-ink);
    font-size: 0.84rem; font-weight: 600;
    padding: 0.4rem 0.9rem; border-radius: var(--radius-sm);
    border: 1px solid var(--border-soft); background: var(--surface);
}
.btn-icon:hover { border-color: var(--accent-ink); }
.calendar-grid {
    display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px;
    background: var(--border-soft);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg); overflow: hidden;
}
.calendar-weekday {
    background: var(--bg); text-align: center;
    font-size: 0.78rem; font-weight: 600; color: var(--muted); padding: 0.5rem 0;
}
.calendar-cell {
    background: var(--surface); min-height: 92px; padding: 0.4rem;
    display: flex; flex-direction: column; gap: 0.25rem;
}
.calendar-cell-empty { background: var(--bg); }
.calendar-cell-today { box-shadow: inset 0 0 0 2px var(--accent); }
.calendar-daynum { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
@media (max-width: 700px) {
    .calendar-cell { min-height: 56px; }
    .calendar-task { font-size: 0.64rem; }
}

/* ---------- Modal ---------- */
.modal {
    position: fixed; inset: 0;
    background: rgba(20, 20, 30, 0.45);
    display: flex; align-items: center; justify-content: center; z-index: 50;
}
.modal.hidden { display: none; }
.modal-inner {
    background: var(--surface);
    width: 92vw; height: 88vh;
    border-radius: var(--radius-lg); overflow: hidden;
    display: flex; flex-direction: column; box-shadow: var(--shadow);
}
.modal-header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 0.8rem 1.1rem; border-bottom: 1px solid var(--border-soft); font-weight: 600;
}
.modal-header button {
    border: none; background: var(--bg); color: var(--muted);
    cursor: pointer; font-size: 0.85rem; font-family: inherit;
    padding: 0.35rem 0.9rem; border-radius: 999px;
}
.modal-header button:hover { color: var(--text); }
#modal-frame { flex: 1; border: none; }

/* ---------- Error page ---------- */
.error-page {
    text-align: center; max-width: 420px; margin: 4rem auto;
    display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
}
.error-code { font-size: 3rem; font-weight: 800; color: var(--accent-ink); }
.error-page h1 { margin: 0; }
.error-page p { margin: 0 0 0.5rem; }

/* ---------- Work labels (formerly «اولویت», now «برچسب») ---------- */
.priority-first { background: #fdecea; color: #991b1b; }
.priority-essential { background: #fdefe2; color: #9a3412; }
.priority-non_essential { background: #e9e9ee; color: #4b5563; }
.priority-okr { background: #ede9fe; color: #5b21b6; }

/* ---------- Jalali date picker ---------- */
.jalali-picker { position: relative; display: flex; align-items: center; gap: 0.4rem; }
.jp-display {
    flex: 1;
    text-align: start;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}
.jp-display:hover { border-color: var(--accent-ink); }
.jp-clear {
    border: 1px solid var(--border);
    background: var(--bg);
    color: var(--muted);
    border-radius: var(--radius-sm);
    padding: 0.55rem 0.7rem;
    cursor: pointer;
    font-size: 0.8rem;
    line-height: 1;
}
.jp-clear:hover { color: var(--red); border-color: var(--red); }
.jp-popup {
    display: none;
    position: absolute;
    top: calc(100% + 0.35rem);
    inset-inline-start: 0;
    z-index: 60;
    width: 268px;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    box-shadow: 0 8px 28px rgba(20, 20, 30, 0.15);
    padding: 0.6rem;
}
.jp-popup.jp-open { display: block; }
.jp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; }
.jp-title { font-weight: 700; font-size: 0.88rem; }
.jp-nav {
    border: none; background: var(--bg); color: var(--accent-ink);
    border-radius: 6px; width: 26px; height: 26px;
    cursor: pointer; font-size: 1rem; line-height: 1;
}
.jp-nav:hover { background: var(--accent-soft); }
.jp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.jp-wd { text-align: center; font-size: 0.7rem; color: var(--muted); padding: 0.2rem 0; font-weight: 600; }
.jp-cell {
    border: none; background: none; color: var(--text);
    border-radius: 6px; padding: 0.35rem 0;
    font-family: inherit; font-size: 0.8rem; cursor: pointer;
}
.jp-cell:hover { background: var(--accent-soft); }
.jp-empty { cursor: default; }
.jp-today { box-shadow: inset 0 0 0 1px var(--accent); }
.jp-selected { background: var(--accent); color: var(--on-accent); font-weight: 700; }

/* ---------- Picker helper links ---------- */
.picker-tools { display: flex; gap: 1rem; margin-top: 0.4rem; }
.link-btn {
    border: none; background: none; padding: 0;
    color: var(--accent-ink); font-family: inherit;
    font-size: 0.8rem; cursor: pointer; text-decoration: underline;
}

/* ---------- Colleagues directory ---------- */
.colleague-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--gap-list);
    padding: 0.6rem 0 1rem;
}
.colleague-card {
    display: flex; align-items: center; gap: 0.65rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    background: var(--surface);
}
.colleague-card-avatar {
    width: 38px; height: 38px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0;
}
.colleague-card-text { min-width: 0; }
.colleague-card-name { font-size: 0.87rem; font-weight: 600; }
.colleague-card-role { font-size: 0.75rem; }

/* ---------- Calendar day: add-task affordance ---------- */
.calendar-hint { font-size: 0.8rem; margin: 0 0 0.6rem; }
.calendar-cell-head { display: flex; align-items: center; justify-content: space-between; }
.calendar-add {
    /* Visible at rest: adding a task is what the calendar is for, and a
       control that appears only on hover cannot be found by someone who does
       not already know it is there — and not at all on a touch screen. */
    opacity: 0.6;
    text-decoration: none;
    color: var(--accent-ink);
    font-weight: 700;
    font-size: 0.9rem;
    line-height: 1;
    padding: 0 0.25rem;
    border-radius: 5px;
    transition: opacity 0.12s;
}
.calendar-cell:hover .calendar-add { opacity: 1; }
.calendar-add:hover { background: var(--accent-soft); }


/* ---------- Admin quick actions & row controls ---------- */
.quick-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.btn-ghost {
    background: var(--surface);
    color: var(--accent-ink);
    border: 1px solid var(--accent);
}
.btn-ghost:hover { background: var(--accent-soft); filter: none; }

.row-link {
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    font-size: 0.82rem;
    color: var(--accent-ink);
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
}
.row-link:hover { text-decoration: underline; }
.row-link-danger { color: var(--red-deep); }

.software-desc { font-size: 0.8rem; margin-inline-start: 0.5rem; }
.field-hint { font-size: 0.8rem; line-height: 1.7; margin: 0.4rem 0 0; }
.field-hint code {
    background: var(--bg);
    border-radius: 4px;
    padding: 0.05rem 0.3rem;
    font-size: 0.78rem;
}

/* ---------- Line-icon system ---------- */
.icn { display: block; flex-shrink: 0; }

.topbar-toggle,
.search-mark { gap: 0; }
/* The three controls are circles of one size, so the sign-out carries its
   icon alone; the word lives in its tooltip. */

.stat-icon { color: inherit; display: flex; }
.stat-today .stat-icon { color: var(--green); }
.stat-overdue .stat-icon { color: var(--red); }
.stat-followup .stat-icon { color: var(--blue); }
.stat-letters .stat-icon { color: var(--orange); }

/* Sidebar colleague rows are links into chat now */
a.colleague-item { text-decoration: none; color: inherit; }

/* ---------- Chat ---------- */
/* Chat fills whatever room is left rather than being told a height in advance.
   The old `calc(100vh - var(--topbar-h) - 3rem)` counted the top bar and the page padding,
   but not the tab row added above it later — so the page ended up 74px taller
   than the window, and the compose box's autofocus scrolled it on arrival.
   Letting the column flex means only the message thread ever scrolls, whatever
   sits above it. */
body.section-chat .content {
    display: flex;
    flex-direction: column;
    height: calc(100vh - var(--topbar-h));
    overflow: hidden;
}
.chat-shell {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: 1rem;
    flex: 1;
    min-height: 0;
}
.chat-peers {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow-y: auto;
    padding-bottom: 0.4rem;
}
.chat-peers-head {
    background: var(--surface);
    color: var(--text);
    border-bottom: 1px solid var(--border-soft);
    padding: 0.7rem 1rem;
    font-weight: 800;
    font-size: 0.9rem;
    position: sticky; top: 0;
}
.chat-peer {
    display: flex; align-items: center; gap: 0.6rem;
    padding: 0.55rem 0.9rem;
    text-decoration: none; color: var(--text);
    border-inline-end: 3px solid transparent;
}
.chat-peer:hover { background: var(--bg); }
.chat-peer.active {
    background: var(--accent-soft);
    border-inline-end-color: var(--accent-deep);
}
.chat-peer-avatar {
    width: 34px; height: 34px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0;
}
.chat-peer-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-ink);
    font-size: 0.82rem; font-weight: 700;
}
.chat-peer-public {
    display: flex; align-items: center; justify-content: center;
    background: var(--accent); color: var(--on-accent);
}
.chat-peer-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.chat-peer-name { font-size: 0.86rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-peer-sub { font-size: 0.73rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.chat-main {
    display: flex; flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow: hidden;
    min-width: 0;
}
.chat-head {
    display: flex; align-items: center; gap: 0.7rem;
    padding: 0.7rem 1.1rem;
    border-bottom: 1px solid var(--border-soft);
    flex-shrink: 0;
}
.chat-head-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.chat-head-name { font-weight: 700; font-size: 0.95rem; }
.chat-head-sub { font-size: 0.76rem; color: var(--muted); }

.chat-thread {
    flex: 1;
    overflow-y: auto;
    padding: 1rem 1.1rem;
    display: flex; flex-direction: column; gap: 0.55rem;
    background: var(--bg);
}
.chat-empty { margin: auto; }
.bubble-row { display: flex; align-items: flex-end; gap: 0.5rem; max-width: 78%; }
.bubble-row.theirs { align-self: flex-start; }
.bubble-row.mine { align-self: flex-end; flex-direction: row-reverse; }
.bubble-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    object-fit: cover; flex-shrink: 0;
    font-size: 0.72rem;
}
.bubble {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: 14px;
    padding: 0.5rem 0.8rem;
    min-width: 0;
}
.bubble-row.mine .bubble {
    background: var(--accent);
    border-color: var(--accent-ink);
    color: var(--on-accent);
}
.bubble-sender { font-size: 0.72rem; font-weight: 700; color: var(--accent-ink); margin-bottom: 0.15rem; }
/* Belt and braces: the name is not drawn on your own bubble, and if it
   ever is again, it is white there rather than the colour the bubble
   itself is filled with. */
.bubble-row.mine .bubble-sender { color: var(--on-accent); }
.bubble-body { font-size: 0.89rem; line-height: 1.75; word-wrap: break-word; white-space: pre-wrap; }
.bubble-time { font-size: 0.68rem; opacity: 0.65; margin-top: 0.2rem; text-align: end; }

.chat-compose {
    display: flex; gap: 0.5rem;
    padding: 0.7rem 0.9rem;
    border-top: 1px solid var(--border-soft);
    flex-shrink: 0;
}
.chat-compose input { flex: 1; margin: 0; }
.chat-compose .btn { margin: 0; padding: 0.55rem 0.9rem; }

@media (max-width: 760px) {
    body.section-chat .content { height: auto; overflow: visible; }
    .chat-shell { grid-template-columns: 1fr; flex: none; }
    .chat-peers { max-height: 220px; }
    .chat-thread { min-height: 300px; max-height: 55vh; }
    .bubble-row { max-width: 90%; }
}

/* ---------- User management ---------- */
.row-inactive { opacity: 0.55; }
.row-actions { display: flex; gap: 0.75rem; white-space: nowrap; }
.btn-danger { background: var(--red); }
.btn-danger:hover { background: var(--red); filter: brightness(0.92); }

.choice-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1rem;
    margin: 1rem 0 1.25rem;
}
.choice-card {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
}
.choice-card h3 { margin: 0 0 0.4rem; font-size: 1rem; }
.choice-card p { flex: 1; font-size: 0.85rem; line-height: 1.8; margin: 0; }
.choice-card .btn { align-self: flex-start; }
.choice-danger { border-color: #f3c7c1; }

/* ---------- Calendar: completed tasks ---------- */

/* ---------- Reports: KPI cards ---------- */
.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 0.9rem;
    margin: 1.25rem 0 1.5rem;
}
.kpi {
    background: var(--surface);
    border: 0;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 1.15rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.kpi-cap { font-size: 0.78rem; color: var(--muted); }
.kpi-num { font-size: 1.75rem; font-weight: 800; line-height: 1.25; }
.kpi-sub { font-size: 0.75rem; color: var(--muted); }
/* The first of the four used to be a block of the accent while its three
   neighbours were white — one card in a row of four wearing something else.
   It is white like the rest now, and says it is the first by writing its
   figure in the colour instead. */
/* The mark goes on the card's leading edge, which in this page's
   direction is its right one — a box-shadow cannot be told that, a
   logical border can. */
.kpi-primary { border-inline-start: 3px solid var(--accent); }
.kpi-primary .kpi-num { color: var(--accent-ink); }
.kpi-green { color: var(--green); }
.kpi-red { color: var(--red); }
.kpi-navy { color: var(--navy); }

.report-body { padding: 1rem 1.1rem 1.2rem; }
.report-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
}
@media (max-width: 900px) { .report-columns { grid-template-columns: 1fr; } }

.report-tabs { margin-bottom: 0.6rem; }
.report-tabs .tab {
    background: none; border: none; border-bottom: 2px solid transparent;
    font-family: inherit; cursor: pointer;
}
.range-note { font-size: 0.78rem; margin: 0 0 0.5rem; }

/* ---------- Charts ---------- */
/* A box for the chart to be wider than, on a screen that cannot hold it.
   It does nothing at all on a wide one: no min-width is asked for there, so
   the chart is the width of the panel exactly as before. */
.chart-scroll { width: 100%; }
.chart { width: 100%; }
.chart-svg { width: 100%; height: 170px; display: block; overflow: visible; }
.chart-axis {
    display: grid;
    grid-template-columns: repeat(var(--cols), 1fr);
    margin-top: 0.3rem;
}
.chart-tick { display: flex; flex-direction: column; align-items: center; gap: 0.1rem; min-width: 0; }
.chart-val { font-size: 0.72rem; font-weight: 700; }
.chart-val.is-zero { color: var(--muted); font-weight: 400; }
.chart-lbl {
    font-size: 0.66rem; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
/* Once there are more than a handful of columns a written-out month —
   «۱۸ مرداد», «فروردین» — is wider than the column it belongs to. Stacking it
   over two lines still ran the neighbours together, so the label is turned on
   its side and hung under its own tick: nothing is clipped, nothing touches
   the label beside it, and the month stays written out in full.

   It is taken out of the flow and placed, so a long month name cannot widen
   its column; the space it needs is reserved by the tick's own padding. */
.chart-axis.is-dense .chart-tick { position: relative; padding-bottom: 3.5rem; }
.chart-axis.is-dense .chart-lbl {
    position: absolute; top: 1.15rem; right: 50%;
    /* Its own width, not the column's: an absolutely placed box would
       otherwise be squeezed to the half-column it is anchored in and the text
       would spill out of a box the layout thinks is narrow. */
    width: max-content;
    /* and free of the «max-width: 100%» the flowing label carries, which
       would clamp it back to the width of one column. */
    max-width: none;
    transform-origin: 100% 0;
    /* Downwards and to the left, so the line of each label runs the way the
       words themselves are read. */
    transform: rotate(-55deg);
    white-space: nowrap; overflow: visible; text-overflow: clip;
    font-size: 0.63rem; line-height: 1; text-align: right;
}
.chart-axis.is-dense .chart-val { font-size: 0.66rem; }
/* The turned labels hang below the axis, so the chart keeps room for them. */
.chart-axis.is-dense { padding-bottom: 0.2rem; }

/* ---------- Progress rows ---------- */
.prog-list { display: flex; flex-direction: column; gap: 0.85rem; }
.prog-head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; margin-bottom: 0.3rem; }
.prog-name { font-size: 0.87rem; font-weight: 600; }
.prog-num { font-size: 0.75rem; white-space: nowrap; }
.prog-track { height: 8px; background: var(--bg); border-radius: 999px; overflow: hidden; }
.prog-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width 0.3s; }

.person-row { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.9rem; }
.person-row:last-child { margin-bottom: 0; }
.person-avatar { width: 34px; height: 34px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.person-body { flex: 1; min-width: 0; }

/* ---------- Donut ---------- */
.donut-wrap { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }
.donut { transform: rotate(-90deg); flex-shrink: 0; }
.donut circle { transition: stroke-dasharray 0.3s; }
.donut-num {
    transform: rotate(90deg); transform-origin: 21px 21px;
    text-anchor: middle; font-size: 7px; font-weight: 800; fill: var(--text);
}
.donut-cap {
    transform: rotate(90deg); transform-origin: 21px 21px;
    text-anchor: middle; font-size: 3px; fill: var(--muted);
}
.donut-legend { display: flex; flex-direction: column; gap: 0.4rem; flex: 1; min-width: 120px; }
.legend-row { display: flex; align-items: center; gap: 0.5rem; font-size: 0.82rem; }
.legend-dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.legend-label { flex: 1; }
.legend-num { font-size: 0.78rem; }

/* ---------- SOP shelf ---------- */
.sop-row { align-items: flex-start; gap: 1rem; }
.sop-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.sop-desc { font-size: 0.8rem; }
.sop-files { margin: 0.35rem 0 0; gap: 0.3rem; }
.sop-side { display: flex; align-items: center; gap: 0.85rem; flex-shrink: 0; }

/* A procedure written in the portal: the samples it can start from, the rows
   its steps are typed into, and the chart drawn from those rows. */
/* The two ways onto the shelf, side by side and equally weighted: one files
   a procedure that exists, the other writes one from a piece of text. */
.sop-ways { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.sop-way {
    flex: 1 1 240px;
    display: flex; flex-direction: column; gap: 0.2rem;
    padding: 0.85rem 1rem;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface); color: var(--text); text-decoration: none;
}
.sop-way:hover { border-color: var(--accent); background: var(--accent-soft); }
.sop-way-name { font-weight: 700; }

.sop-samples { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.sop-sample {
    display: flex; flex-direction: column; gap: 0.15rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface); color: var(--text); text-decoration: none;
}
.sop-sample:hover { border-color: var(--accent); background: var(--accent-soft); }
.sop-sample.is-on { border-color: var(--accent); background: var(--accent-soft); }
.sop-sample-name { font-weight: 600; }

.sop-steps { display: flex; flex-direction: column; gap: 0.5rem; }
.sop-step-row {
    display: grid; align-items: center; gap: 0.5rem;
    grid-template-columns: 1.6rem minmax(0, 2.4fr) minmax(0, 1.2fr) 7rem 9rem;
}
.sop-step-row input, .sop-step-row select { margin: 0; }
.sop-step-no { color: var(--muted); text-align: center; }
@media (max-width: 760px) {
    .sop-step-row { grid-template-columns: 1.6rem minmax(0, 1fr); }
}

.sop-step-list { margin: 0; padding-inline-start: 1.4rem; }
.sop-step-item { margin-bottom: 0.5rem; display: flex; flex-wrap: wrap;
                 align-items: center; gap: 0.5rem; }

.sop-flow-box { overflow-x: auto; padding: 0.4rem 0; }
.sop-flow { max-width: 640px; margin: 0 auto; display: block; font-family: inherit; }
.sop-flow text { font-size: 14px; }
.sop-flow .sop-flow-owner { font-size: 12px; opacity: 0.85; }
.sop-flow .sop-flow-branch { font-size: 12px; font-weight: 600; }

/* ---------- Software data-scope control ---------- */
.scope-form { flex-shrink: 0; }
.scope-form select {
    width: auto;
    padding: 0.25rem 0.5rem;
    font-size: 0.78rem;
    border-radius: 999px;
    background: var(--bg);
}

/* ---------- Live monitor & change log ---------- */
.panel-head-live::before {
    content: "";
    display: inline-block;
    width: 8px; height: 8px;
    margin-inline-end: 8px;
    border-radius: 50%;
    background: var(--green);
    box-shadow: 0 0 0 0 rgba(39, 165, 103, 0.5);
    animation: live-pulse 2s infinite;
}
@keyframes live-pulse {
    70%  { box-shadow: 0 0 0 7px rgba(39, 165, 103, 0); }
    100% { box-shadow: 0 0 0 0 rgba(39, 165, 103, 0); }
}
/* A steady dot for anyone who finds the pulse distracting. */
@media (prefers-reduced-motion: reduce) {
    .panel-head-live::before { animation: none; }
}

.live-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 0.7rem;
    padding: 1rem 1.1rem;
}
.live-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    background: var(--bg);
}
.live-dot {
    flex: none;
    width: 9px; height: 9px;
    border-radius: 50%;
    background: var(--green);
}
.live-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.live-name { font-weight: 600; font-size: 0.88rem; }
.live-who .muted { font-size: 0.76rem; }
.live-ago { margin-inline-start: auto; font-size: 0.74rem; white-space: nowrap; }

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1rem;
}
.filter-bar select {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: #fff;
    font: inherit;
    font-size: 0.85rem;
    color: var(--text);
}

.pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.9rem;
    margin-top: 1.1rem;
}

.nowrap { white-space: nowrap; }
/* A refused attempt should stand out from ordinary traffic in the log. */
.row-denied { background: var(--accent-soft); }

/* Explanatory line under a form field that needs one. */
.field-note {
    /* The note explains the field above it, so it needs to read as attached to
       that field — but pulled flush against the control it looked like part of
       it rather than a remark about it. This leaves a small gap above and a
       larger one below, so the grouping stays obvious. */
    margin: 0.45rem 0 1.15rem;
    font-size: 0.78rem;
    line-height: 1.9;
}

/* ---------- Organisation chart ----------
 *
 * Drawn top-down: a manager sits above their people, who stand side by side
 * underneath. The earlier version stacked every level in a single column with
 * an indent, which is how a file tree reads — fine for folders, wrong for an
 * organisation, because the one thing the chart exists to show is who is at
 * the same level as whom, and a column hides exactly that.
 *
 * The connecting lines are drawn with pseudo-elements rather than an image or
 * a canvas, so they follow the cards when text reflows. Everything is written
 * with logical properties (inline-start/end rather than left/right) so the
 * whole chart mirrors correctly in Persian.
 */
.org-scroll {
    overflow-x: auto;
    /* Enough room that the outermost cards and their lines are never clipped,
       and the chart does not sit flush against the panel edge. */
    padding: 1.6rem 0 1.9rem;
}
.org-tree, .org-children { list-style: none; margin: 0; padding: 0; }

/* Roots stand beside each other; an organisation may have more than one head. */
.org-tree {
    display: flex;
    /* `safe` matters: a plain `center` on content wider than the panel pushes
       the far edge out past the scroll origin, where it cannot be reached.
       With `safe` the chart centres while it fits and falls back to aligning
       from the start once it has to scroll. */
    justify-content: safe center;
    align-items: flex-start;
    gap: 0.4rem;
    min-width: max-content;
}

.org-children {
    display: flex;
    justify-content: safe center;
    align-items: flex-start;
    position: relative;
    /* The gap the connecting lines are drawn into. */
    padding-top: 1.35rem;
}

.org-node {
    position: relative;
    padding: 1.35rem 0.4rem 0;
    text-align: center;
}

/* Each child draws the two halves of the horizontal bar above it, plus the
   short drop into its own card. */
.org-node::before,
.org-node::after {
    content: "";
    position: absolute;
    top: 0;
    width: 50%;
    height: 1.35rem;
    border-top: 2px solid var(--border);
}
.org-node::before { inset-inline-end: 50%; }
.org-node::after  { inset-inline-start: 50%; border-inline-start: 2px solid var(--border); }

/* The bar must not overhang the outermost children, and the corners are
   rounded so the joins do not read as a broken line. */
.org-node:first-child::before { border: 0; }
.org-node:last-child::after   { border-top: 0; }
.org-node:first-child::after  { border-start-start-radius: 6px; }
.org-node:last-child::before  {
    border-inline-end: 2px solid var(--border);
    border-start-end-radius: 6px;
}

/* An only child needs no bar at all — just the drop from its manager. */
.org-node:only-child::before { display: none; }
.org-node:only-child::after {
    inset-inline-start: 50%;
    border-top: 0;
    border-radius: 0;
}

/* The line leaving a manager on its way down to the row of their people. */
.org-children::before {
    content: "";
    position: absolute;
    top: 0;
    inset-inline-start: 50%;
    width: 0;
    height: 1.35rem;
    border-inline-start: 2px solid var(--border);
}

/* Roots hang from nothing, so they carry neither drop nor bar. */
.org-tree > .org-node { padding-top: 0; }
.org-tree > .org-node::before,
.org-tree > .org-node::after { display: none; }


.org-chain {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem;
    margin: 0 0 0.7rem;
}
.org-chain-step {
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    background: var(--bg);
    font-size: 0.82rem;
}
.org-chain-step.is-me { background: var(--accent); color: var(--on-accent); font-weight: 600; }
.org-chain-arrow { color: var(--muted); }

.panel-head-warn::before { background: var(--orange); }

/* Unread counts shown on a tab rather than a sidebar row. */
.tab-badge {
    display: inline-block;
    min-width: 1.25rem;
    margin-inline-start: 0.35rem;
    padding: 0.05rem 0.35rem;
    border-radius: 999px;
    background: var(--accent);
    color: var(--on-accent);
    font-size: 0.7rem;
    line-height: 1.5;
    text-align: center;
}
.tab.active .tab-badge { background: #fff; color: var(--accent-ink); }

/* Per-unit KPI table: the selected unit, and figures that need attention. */
.row-selected { background: var(--accent-soft); }
/* A unit in trouble, marked on the row itself. A tint alone carries no meaning
   for anyone who cannot separate the two colours, so the badge in the row says
   it in words as well and this only helps the eye find the row. */
.row-critical { background: #fdf3f1; }
.row-critical td:first-child { box-shadow: inset -3px 0 0 var(--red-deep); font-weight: 600; }
/* The deeper red, because this one is *read*: a lateness in a table cell
   is 14px, and the flat red comes to 3.85:1 on white — under the 4.5
   small text needs. The deep one measures 5.6:1. */
.cell-warn { color: var(--red-deep); font-weight: 600; }
.prog-inline {
    display: inline-block;
    width: 70px;
    vertical-align: middle;
    margin-inline-end: 0.4rem;
}

/* KPI status pills: met, missed, not yet reported, or not scored at all. */
.kpi-orange { color: var(--orange); }

/* A table inside a panel belongs to that panel. .table-wrap carries its own
   background, border, radius and shadow so it can stand alone elsewhere; left
   in place inside a panel it drew a second card stacked on the first, with the
   panel body's padding showing through as a step between the coloured header
   and the table. Strip the chrome and cancel the padding so the two read as
   one piece. */
.panel-body > .table-wrap {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    margin-block: -0.4rem;
}

/* The colleagues panel heading doubles as the way to the full list. */
.side-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.side-panel-link {
    /* Same heading, same ground: the head is white now, so the link beside
       the title is written in the colour rather than in white. When it is a
       <button> it also has to lose the browser's own grey ground and border. */
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--accent-ink);
    text-decoration: none;
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    cursor: pointer;
}
.side-panel-link:hover { text-decoration: underline; }

/* ---------- Unit report forms ---------- */
/* The paper forms are sections of related questions, so the fields sit in a
   grid that keeps short answers side by side and gives prose its own row. */
.field-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 0.9rem 1.1rem;
    padding: 1rem 0;
}
.field-cell { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.field-wide { grid-column: 1 / -1; }
.field-label { font-size: 0.82rem; font-weight: 600; color: var(--text); }
.field-cell input,
.field-cell select,
.field-cell textarea {
    width: 100%;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    font: inherit;
    font-size: 0.86rem;
    background: #fff;
    color: var(--text);
}
.field-cell textarea { resize: vertical; }
/* A value the form states rather than asks for. */
.field-static .field-fixed {
    padding: 0.5rem 0.7rem;
    border: 1px dashed var(--border);
    border-radius: 8px;
    background: var(--bg);
    font-size: 0.86rem;
    color: var(--muted);
}

/* A unit suffix that belongs to the input rather than the label. */
.input-affix { display: flex; align-items: stretch; }
.input-affix input { border-start-start-radius: 0; border-end-start-radius: 0; }
.input-affix .affix {
    display: flex;
    align-items: center;
    padding: 0 0.6rem;
    border: 1px solid var(--border);
    border-inline-end: 0;
    border-start-start-radius: 8px;
    border-end-start-radius: 8px;
    background: var(--bg);
    font-size: 0.82rem;
    color: var(--muted);
}

.grid-table td { padding: 0.35rem 0.4rem; }
.grid-table input,
.grid-table select,
.grid-table textarea {
    width: 100%;
    min-width: 90px;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 7px;
    font: inherit;
    font-size: 0.84rem;
    background: #fff;
}
.grid-table textarea { min-height: 2.4rem; resize: vertical; }
.grid-num { width: 2.4rem; text-align: center; color: var(--muted); font-size: 0.8rem; }
.grid-add { margin: 0.7rem 0 1rem; }

.form-note { padding: 0.85rem 0 0; margin: 0; line-height: 1.9; }
.form-actions {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: 1.25rem 0 2rem;
}
.req { color: var(--red); }

/* A filed report, read back. */
.sub-head {
    margin: 1rem 0 0.4rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--text);
}
.answer-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.7rem 1.1rem;
    margin: 0;
    padding: 0.8rem 1.1rem;
}
.answer { min-width: 0; }
.answer dt { font-size: 0.78rem; color: var(--muted); margin-bottom: 2px; }
.answer dd { margin: 0; font-size: 0.88rem; line-height: 1.8; word-break: break-word; }

/* An indicator the portal worked out for itself. */

/* The way back out of a long form, above its title. */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.6rem;
    padding: 0.35rem 0.75rem 0.35rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: #fff;
    color: var(--muted);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.back-link:hover { color: var(--accent-ink); border-color: var(--accent-ink); }

/* Areas added after the original palette. */

/* ---------- Status pills ----------
 *
 * .badge is a soft tint: a pale background with the same hue as its text. The
 * colour variants used to set only the background, so a red pill kept the
 * page's own accent for its text — indigo lettering on red, which could not be
 * read at all. Each variant now sets both halves of the pair.
 */
.badge-green,  .badge-create,  .badge-kpi-met {
    background: #e6f6ee; color: #16764a;
}
.badge-red,    .badge-delete,  .badge-kpi-missed {
    background: #fdecea; color: #b03626;
}
.badge-orange, .badge-denied,  .badge-kpi-empty {
    background: #fdefe2; color: #9a5310;
}
.badge-blue,   .badge-update {
    background: #e7f1fd; color: #1c5fb4;
}
.badge-navy,   .badge-login,   .badge-auto {
    background: #e5eef6; color: #1d4f78;
}
.badge-logout, .badge-kpi-unscored {
    background: #f0f0f4; color: #5b5b67;
}

/* A description that follows a pill belongs on its own line: inline, it wrapped
   around the pill and the two ran into each other. */
.software-desc {
    display: block;
    margin-inline-start: 0;
    margin-top: 0.25rem;
    line-height: 1.9;
}

/* ---------- Files sent in chat ---------- */
.chat-attach {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: 10px;
    color: var(--muted);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.chat-attach:hover { background: var(--bg); color: var(--accent-ink); }

/* What is queued to send, so a chosen file is not invisible. */
.chat-chosen {
    padding: 0 1.1rem 0.6rem;
    font-size: 0.78rem;
    color: var(--muted);
}

.bubble-file {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.35rem;
    padding: 0.35rem 0.55rem;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.06);
    color: inherit;
    text-decoration: none;
    font-size: 0.8rem;
}
.bubble-file:hover { background: rgba(0, 0, 0, 0.12); }
.bubble-file-name { overflow-wrap: anywhere; }
/* On the sender's own bubble the ground is the accent colour, so the tint has
   to lift rather than darken. */
.bubble-row.mine .bubble-file { background: rgba(255, 255, 255, 0.22); }
.bubble-row.mine .bubble-file:hover { background: rgba(255, 255, 255, 0.34); }

/* ---------- Signatures on an administrative form ----------
 *
 * Two seats sign each request. Whoever is being waited on gets a control;
 * everyone else gets the same row reading «در انتظار تایید», so a person can
 * follow their own request without being able to answer it.
 */
.approval-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem 2rem;
    padding: 0.2rem 0 0.6rem;
}
.approval-item { display: flex; align-items: center; gap: 0.6rem; }
.approval-label { font-size: 0.82rem; color: var(--muted); }
.approval-form { display: flex; align-items: center; gap: 0.4rem; }
.approval-form select { min-width: 150px; }

.approval-chip {
    display: inline-block;
    padding: 0.2rem 0.7rem;
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
}
/* Soft grounds with matching text, the same pairing the badges use, so the
   word stays readable rather than sitting on a solid block of colour. */
.approval-pending { background: #fdf1e0; color: #8a5a12; }
.approval-yes     { background: #e6f4ec; color: #1d7a4c; }
.approval-no      { background: #fdecea; color: #a8331f; }

.approval-verdict { margin-inline-start: 0.5rem; }
.btn-sm { padding: 0.35rem 0.8rem; font-size: 0.8rem; }

/* ---------- Recording a voice note ---------- */
.voice { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.voice-inline { flex: none; }

.voice-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: none;
    padding: 0;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.voice-btn:hover { background: var(--bg); color: var(--accent-ink); }

/* While recording the button turns red and pulses, so there is no doubt the
   microphone is live — the one state a person must never be unsure about. */
.voice.is-recording .voice-btn {
    background: var(--red);
    color: #fff;
    animation: voice-pulse 1.4s ease-in-out infinite;
}
@keyframes voice-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(224, 82, 63, 0.45); }
    50%      { box-shadow: 0 0 0 6px rgba(224, 82, 63, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .voice.is-recording .voice-btn { animation: none; }
}

.voice-timer {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--red);
    font-variant-numeric: tabular-nums;
}
.voice-preview { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.voice-audio { height: 34px; max-width: 240px; }
.voice-drop {
    border: 0;
    background: transparent;
    color: var(--red);
    font-size: 0.78rem;
    cursor: pointer;
    padding: 0.2rem 0.4rem;
}
.voice-error, .voice-unavailable { font-size: 0.78rem; }
.voice-error { color: var(--red); }

/* A voice note in a message reads as the message, not as an attachment. */
.bubble-voice { display: block; height: 36px; max-width: 260px; margin-top: 0.3rem; }

/* ---------- Weekly meetings ---------- */
/* The form sits inside a panel body, which has no side padding of its own
   because ordinary rows and tables inset themselves. A form does not, so it
   was left with its labels and fields against the panel border — this gives it
   the same 1.1rem inset the rest of the portal reads at. */
.meeting-form {
    margin-bottom: 0;
    box-shadow: none;
    border: 0;
    padding: 0.5rem 1.1rem 0.9rem;
}

/* Same reason, for what a filed meeting shows: the player, the transcript and
   the buttons are all dropped straight into the panel body. */
.meeting-panel .panel-body { padding: 0.9rem 1.1rem 1rem; }
.meeting-panel .inline-form { margin-bottom: 0; }
.meeting-panel .inline-form + .inline-form { margin-top: 0.6rem; }
.meeting-meta { margin: 0 0 0.6rem; font-size: 0.82rem; }
.meeting-audio { display: block; width: 100%; max-width: 460px; margin-bottom: 0.8rem; }

.meeting-text > summary {
    cursor: pointer;
    font-size: 0.86rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
}
/* The transcript is the point of the page, so it is given room to be read:
   the speaker's own line breaks kept, wrapped rather than scrolled sideways. */
.transcript {
    margin: 0;
    /* A <pre> is drawn in the browser's monospace face unless it is told
       otherwise, which is why the minute alone read in a different type. */
    font-family: inherit;
    padding: 0.9rem 1rem;
    max-height: 420px;
    overflow-y: auto;
    background: var(--bg);
    border: 1px solid var(--border-soft);
    border-radius: 10px;
    font-size: 0.86rem;
    line-height: 2;
    white-space: pre-wrap;
    word-break: break-word;
}
.transcript-error {
    direction: ltr;
    text-align: left;
    font-size: 0.76rem;
    color: var(--red);
    max-height: 180px;
}

/* Taking back a message: kept quiet until the bubble is pointed at, so a
   thread does not read as a column of delete buttons. */
.bubble-time { display: flex; align-items: center; gap: 0.5rem; }
.bubble-row.mine .bubble-time { justify-content: flex-end; }
.bubble-delete { display: inline; margin: 0; }
.bubble-delete button {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-size: 0.7rem;
    cursor: pointer;
    color: inherit;
    opacity: 0;
    transition: opacity 0.15s ease;
    text-decoration: underline;
}
.bubble:hover .bubble-delete button,
.bubble-delete button:focus-visible { opacity: 0.8; }
.bubble-delete button:hover { opacity: 1; }
/* Touch has no hover, so there it simply stays visible. */
@media (hover: none) {
    .bubble-delete button { opacity: 0.65; }
}

/* ---------- Notifications ---------- */
.topbar-disc { position: relative; }

/* The count sits on the bell rather than beside it, so the bar keeps its
   rhythm whether there is anything waiting or not. */
.bell-count {
    position: absolute;
    top: -6px;
    inset-inline-end: -8px;
    min-width: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--red-deep);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
}

.notif-row { align-items: flex-start; }
.notif-icon { color: var(--muted); display: flex; padding-top: 2px; }
.notif-body { display: block; font-size: 0.8rem; margin-top: 2px; }
/* Unread carries a coloured edge and a tint, so the eye finds what is new
   without having to read every line. */
.notif-row.is-unread {
    background: var(--accent-soft);
    border-inline-start: 3px solid var(--accent);
}
.notif-row.is-unread .line-title { font-weight: 600; }
.notif-row.is-unread .notif-icon { color: var(--accent-ink); }

.notif-help { margin: 0.7rem 0 0; font-size: 0.8rem; line-height: 1.9; }
.notif-test { margin: 0; }
#enable-push.is-on { color: var(--green-deep); border-color: var(--green-deep); }



/* ---------- Electronic signature ---------- */
/* A panel body has no side padding of its own — rows and tables inset
   themselves — so a panel made of plain content has to supply it, or the
   labels and fields sit against the border. Same reason as the meetings page. */
.sig-body { padding: 0.9rem 1.1rem 1rem; }
.sig-body > * { margin-bottom: 0.9rem; }
.sig-body > *:last-child { margin-bottom: 0; }
.sig-body > p { padding-inline: 0; }
.sig-pad { max-width: 420px; }
/* A pad you can actually sign on with a finger: wide, short, and clearly a
   place to write rather than a form field. */
.sig-canvas {
    width: 100%;
    height: 140px;
    display: block;
    background: #fff;
    border: 1px dashed var(--border);
    border-radius: 10px;
    touch-action: none;          /* the finger draws, it does not scroll */
    cursor: crosshair;
}
.sig-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.6rem; flex-wrap: wrap; }
.sig-status { font-size: 0.78rem; }
.sig-status.is-bad { color: var(--red-deep); }
.sig-current { display: flex; align-items: center; gap: 0.8rem; }
.sig-preview {
    height: 56px; max-width: 220px; object-fit: contain;
    border: 1px solid var(--border-soft); border-radius: 8px; padding: 4px;
    background: #fff;
}

/* On a signed form the mark sits beside the decision, with its provenance
   underneath in small type — the part that actually carries the weight. */
.sig-block { display: inline-flex; align-items: center; gap: 0.5rem; }
.sig-stamp {
    height: 34px; max-width: 130px; object-fit: contain;
    background: #fff; border-radius: 6px; padding: 2px;
}
.sig-meta { font-size: 0.74rem; }

.sig-broken {
    margin: 0 1.1rem 0.8rem;
    padding: 0.6rem 0.9rem;
    border-radius: 10px;
    background: #fdecea;
    color: var(--red-deep);
    font-size: 0.82rem;
    font-weight: 600;
}


/* ---------- Changing your own password ---------- */
/* Not a card of its own: it sits inside one, and the card's ground and inset
   are already there. Left with the form-box's white fill and rounded corners
   it read as a second card drawn on top of the first. */
.pw-form {
    box-shadow: none; border: 0; padding: 0; margin-bottom: 0; max-width: 420px;
    background: none; border-radius: 0;
}
.pw-ok {
    margin: 0 0 0.9rem;
    padding: 0.6rem 0.9rem;
    border-radius: 10px;
    background: #e6f4ec;
    color: var(--green-deep);
    font-size: 0.85rem;
    font-weight: 600;
}


/* ---------- KPI: units folded shut ----------
 *
 * Twelve units open at once is a wall of tables nobody reads. Closed, the
 * page is a dozen lines and the reader picks. The heading has to carry enough
 * to pick on, so it holds the standing and the reason as well as the name.
 */
.unit-fold { margin-bottom: 0.7rem; }
.unit-summary {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
/* The default disclosure triangle sits on the wrong side in an RTL page and
   cannot be styled, so it is removed and replaced with one of our own. */
.unit-summary::-webkit-details-marker { display: none; }
.unit-summary::marker { content: ""; }

.unit-name { font-weight: 700; flex: none; }
.unit-why {
    flex: 1;
    min-width: 0;
    font-size: 0.78rem;
    font-weight: 400;
    opacity: 0.85;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.unit-chevron { flex: none; transition: transform 0.15s ease; }
.unit-fold[open] .unit-chevron { transform: rotate(180deg); }

/* A critical unit is findable without reading: the whole card is edged. */
.unit-fold.is-critical { box-shadow: inset 3px 0 0 0 var(--red-deep), var(--shadow); }

.action-row { align-items: flex-start; }
.action-row .software-desc { display: block; margin-top: 2px; }
/* An indicator whose sheet never said what to do about a miss — worth showing
   as a gap rather than hiding, because that gap is itself the finding. */
.action-row .software-desc.is-missing { color: var(--orange); }
.unreported-note { padding: 0 1.1rem; font-size: 0.82rem; }
@media (prefers-reduced-motion: reduce) { .unit-chevron { transition: none; } }

/* ---------- میز کار: a second sidebar inside the section ---------- */
/* The desk is several places rather than one page, so it carries its own
   column beside the portal's. Sticky, because the list of tasks below it is
   long and the way out of it should not be at the top of a scroll. */
.work-shell {
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
}
.work-side {
    width: 190px;
    flex-shrink: 0;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow: hidden;
    position: sticky;
    top: calc(var(--topbar-h) + 16px);
}
.work-nav { padding: 0.35rem 0; }
.work-link {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 0.9rem;
    text-decoration: none;
    color: #52525c;
    font-size: 0.9rem;
    font-weight: 500;
    border-inline-end: 0;
    transition: color 0.14s ease;
}
.work-link:hover { color: var(--text); }
/* The same rule as the main menu: the row you are on is told by its icon
   alone, filled in the house colour; the label only darkens and gets
   heavier. No band, no edge. */
.work-link.active {
    background: none;
    color: var(--text);
    font-weight: 700;
}
.work-link.active .work-icon {
    background: var(--accent);
    color: #fff;
    box-shadow: 0 4px 10px rgba(79, 70, 229, 0.28);
}
/* Each row keeps its own icon colour, as in the reference: the colour is
   how a row is recognised at a glance, and the tile is small enough that
   carrying five of them costs the eye nothing. */
.work-icon {
    width: 30px; height: 30px;
    border-radius: 9px;
    transition: background 0.16s ease, color 0.16s ease;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.work-icon-home     { background: rgba(224, 82, 63, 0.12);  color: var(--red-deep); }
.work-icon-chat     { background: rgba(47, 128, 237, 0.12); color: var(--navy); }
.work-icon-projects { background: rgba(232, 114, 28, 0.12); color: #9a4a09; }
.work-icon-tasks    { background: rgba(29, 122, 76, 0.12);  color: var(--green-deep); }
.work-icon-letters  { background: rgba(232, 114, 28, 0.12); color: #9a4a09; }
.work-icon-monitoring { background: rgba(36, 96, 143, 0.12); color: var(--navy); }

/* پروژه‌ها has no page yet. It reads as a row that is not ready rather than
   as a link that is broken. */
.work-link.is-soon { cursor: default; color: var(--muted); }
.work-link.is-soon:hover { background: transparent; }
.work-link.is-soon .work-icon { opacity: 0.55; }
.work-soon {
    font-size: 0.68rem;
    color: var(--muted);
    border: 1px solid var(--border-soft);
    border-radius: 5px;
    padding: 0.05rem 0.3rem;
}

.work-side .side-panel { border-top: 1px solid var(--border-soft); }

.work-main { flex: 1; min-width: 0; }

/* Chat sizes itself to the window, so its column has to stretch rather than
   sit at its natural height. */
body.section-chat .work-shell { flex: 1; min-height: 0; align-items: stretch; }
body.section-chat .work-side { position: static; align-self: flex-start; }
body.section-chat .work-main { display: flex; flex-direction: column; min-height: 0; }

/* 700, not 900. At 900 a 768px tablet lost this column and got the whole
   colleague list stacked above every page; 190px of nav beside 554px of page
   fits a tablet comfortably. Below 700 it does have to stack. */
@media (max-width: 700px) {
    /* Two columns do not fit on a phone; the desk's own nav becomes a row
       above the page, which is what it was before. */
    .work-shell { display: block; }
    .work-side {
        width: auto;
        position: static;
        margin-block-end: 1rem;
    }
    .work-nav { display: flex; overflow-x: auto; padding: 0; }
    /* The nav becomes a row on a phone; the colleagues list underneath it
       stays a column. */
    .work-side .side-panel { border-top: none; }
    .work-link {
        border-inline-end: none;
        white-space: nowrap;
    }
}

/* ---------- کارتابل: labels, filter, row furniture ---------- */
/* A label is read at a glance in a list of thirty letters, so it is a filled
   chip rather than an outline: the colour has to survive being 12px tall next
   to a wall of text. Each pairs a pale ground with its own deep ink, every
   pair measured past 4.5:1 rather than picked by eye. */
.chip {
    /* A flex box of its own, with a line height of its own: as an inline box
       its height followed the text inside it, so a chip whose label is Latin
       («OKR») sat a few pixels above the Persian ones beside it. Now every
       chip is the same height and every row of them reads as one line. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 1.55rem;
    line-height: 1.2;
    padding: 0.12rem 0.5rem;
    border-radius: 6px;
    font-size: 0.74rem;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    border: 1px solid transparent;
    vertical-align: middle;
}
.chip-red    { background: #fdecea; color: #a32b1c; }
.chip-orange { background: #fdf0e3; color: #8a4a06; }
.chip-green  { background: #e7f4ec; color: #14603b; }
.chip-blue   { background: #e8f0fd; color: #1c4e8f; }
.chip-purple { background: #f0eafc; color: #57309b; }
.chip-teal   { background: #e6f2f1; color: #14605c; }
.chip-grey   { background: #eceef1; color: #43474e; }
/* The label the list is currently filtered by. */
.chip-on { border-color: currentColor; }

.letter-tags {
    display: flex; align-items: center;
    /* No width cap: a row of labels is read whole or not at all, and a cap
       hid the third one behind the edge of a box nobody could scroll. */
    gap: 0.3rem; flex-shrink: 0;
}
.letter-subject { text-decoration: none; display: block; }
.letter-subject:hover { text-decoration: underline; }
.letter-clip { display: inline-flex; align-items: center; color: var(--muted); }
.letter-row .inline-form { margin-bottom: 0; }
.letter-side .line-date { color: var(--muted); white-space: nowrap; }

/* The filter sits under the boxes and applies within whichever box is open. */
.letter-filter {
    display: flex; align-items: center; gap: 0.6rem;
    margin: 0 0 1rem;
}
.letter-filter input[type="search"] { max-width: 320px; }

/* The labels panel hangs off the row of boxes, as in the reference. The tab
   row normally scrolls sideways, and an overflow container clips anything
   absolutely positioned inside it — which silently swallowed this panel — so
   this one row wraps instead of scrolling. */
.tabs-cartable { align-items: center; overflow: visible; flex-wrap: wrap; }
.label-menu { position: relative; }
.label-menu > summary { list-style: none; cursor: pointer; }
.label-menu > summary::-webkit-details-marker { display: none; }
.label-panel {
    position: absolute;
    inset-inline-start: 0;
    top: calc(100% + 6px);
    z-index: 20;
    width: 320px;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    box-shadow: 0 8px 28px rgba(20, 20, 30, 0.14);
    padding: 0.8rem 0.9rem;
    display: flex; flex-direction: column; gap: 0.55rem;
}
.label-panel-list { display: flex; flex-direction: column; gap: 0.4rem; }
.label-panel-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.label-edit > summary {
    list-style: none; cursor: pointer;
    color: var(--muted); font-size: 0.85rem;
}
.label-edit > summary::-webkit-details-marker { display: none; }
.label-form {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem;
    margin-top: 0.4rem;
}
.label-form input[type="text"] { flex: 1; min-width: 110px; }
.label-form select { width: auto; }
.label-new { border-top: 1px solid var(--border-soft); padding-top: 0.6rem; }

/* Putting labels on one letter, from the letter's own page. */
/* Chips sit on one line with each other. Without this an item whose text is
   Latin — «OKR» — makes a taller box than the Persian ones beside it and ends
   up riding above the row; centring puts every chip on the same line whatever
   its own height. */
.label-picker-list {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 0.6rem; margin-bottom: 0.8rem;
}
.chip-check {
    /* A chip is not a form label with a field under it: the `form label` rule
       gives every label a top margin and drops it for the first one, which
       inside a wrapping row of chips put the first chip of each row a line
       above the others — «OKR» and «ابلاغیه» rode high for exactly that
       reason. The margins are cleared here so a row of chips is a row.
       Line-height zero kills the stray line box the whitespace around the
       chip would otherwise make; the chip carries its own. */
    display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer;
    line-height: 0;
    margin: 0;
}
.chip-check input { margin: 0; }
.letter-actions { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1rem; }
.letter-actions .inline-form { margin-bottom: 0; }

@media (max-width: 700px) {
    .label-panel { width: min(320px, 80vw); }
}

/* Two buttons where the page head expects one. */
.head-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* ---------- ارسال نامه: the compose form ---------- */
/* One row per field, label beside control, with the optional parts folded
   away behind a + until they are wanted — the shape of the reference. */
.compose {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 1.2rem 1.4rem 0;
    max-width: 760px;
}
.compose-head h1 { margin-bottom: 0.2rem; }
.compose-row {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 0.9rem 0;
    border-block-end: 1px solid var(--border-soft);
}
.compose-label {
    width: 90px;
    flex-shrink: 0;
    padding-top: 0.45rem;
    font-size: 0.9rem;
    font-weight: 600;
}
.compose-field { flex: 1; min-width: 0; }
.compose-field input[type="text"],
.compose-field textarea { width: 100%; }
.compose-row-body textarea { min-height: 160px; }

/* A folded section: the summary is the row, the + is the affordance. */
.compose-fold { display: block; }
.compose-fold > summary {
    display: flex; align-items: center; gap: 1rem;
    cursor: pointer; list-style: none;
}
.compose-fold > summary::-webkit-details-marker { display: none; }
.compose-fold .compose-label { padding-top: 0; }
.compose-plus { color: var(--muted); font-size: 1.1rem; line-height: 1; }
.compose-fold[open] .compose-plus { transform: rotate(45deg); }
.compose-fold[open] > .compose-field { margin-top: 0.8rem; }
.compose-fold > .compose-field { margin-inline-start: calc(90px + 1rem); }
.compose-inline { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.6rem; }
.compose-check { display: flex; align-items: center; gap: 0.45rem; }
.compose-foot {
    display: flex; align-items: center; gap: 1.2rem;
    padding: 1rem 0 1.2rem;
}
.field-error { color: var(--red-deep); font-size: 0.8rem; margin: 0.3rem 0 0; }
.label-add { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-top: 0.7rem; }
.label-add input[type="text"] { max-width: 220px; }
.label-add select { width: auto; }

@media (max-width: 700px) {
    .compose-row { flex-direction: column; gap: 0.4rem; }
    .compose-label { width: auto; padding-top: 0; }
    .compose-fold > .compose-field { margin-inline-start: 0; }
}

/* A day the field will not accept: shown, so the month still reads as a
   month, but plainly out of reach. */
.jp-cell.jp-blocked { color: var(--muted); opacity: 0.4; cursor: default; }

/* A KPI card that is a link. It says so by lifting on hover rather than by
   turning blue: the row of cards has to keep reading as one row. */
.kpi-link { text-decoration: none; color: inherit; transition: box-shadow 0.15s, transform 0.15s; }
.kpi-link:hover { box-shadow: 0 4px 16px rgba(20, 20, 30, 0.10); transform: translateY(-1px); }
.kpi-link .kpi-sub { color: var(--accent-ink); }

/* Sending a meeting record on, and what came back. */
.meeting-share, .meeting-replies { margin-top: 0.9rem; }
.meeting-share > summary, .meeting-replies > summary {
    cursor: pointer; font-size: 0.88rem; font-weight: 600; color: var(--accent-ink);
}
.meeting-share-body { padding-top: 0.7rem; }
.meeting-share-body label { display: block; margin: 0.6rem 0 0.3rem; font-size: 0.85rem; font-weight: 600; }
/* …except the tick rows, which lay themselves out. This rule is more specific
   than «.unit-check» and was quietly undoing its grid, so the box and its
   name stopped lining up. */
.meeting-share-body .unit-check {
    display: grid;
    margin: 0;
    font-weight: 400;
    font-size: 0.88rem;
}
/* One unit to a line: the box first, then the name. Laid out as a grid
   rather than a wrapping row so every box sits in the same column and every
   name starts at the same place. */
.unit-checks { display: grid; gap: 0.35rem; margin-bottom: 0.9rem; }
.unit-check {
    display: grid; grid-template-columns: 1.1rem 1fr;
    align-items: center; gap: 0.55rem;
    font-size: 0.88rem; cursor: pointer;
}
.unit-check input { margin: 0; }
.unit-check-name { min-width: 0; }
.reply-row {
    border-inline-start: 3px solid var(--accent-soft);
    padding: 0.4rem 0.7rem;
    margin-bottom: 0.6rem;
}
.reply-body { white-space: pre-wrap; margin: 0.25rem 0 0.4rem; }

/* ---------- پروژه‌ها ---------- */
.project-row { gap: 0.8rem; }
.project-progress { display: flex; align-items: center; gap: 0.45rem; flex-shrink: 0; }
.progress-bar {
    width: 90px; height: 7px;
    background: var(--border-soft);
    border-radius: 999px;
    overflow: hidden;
}
.progress-fill { display: block; height: 100%; background: var(--accent); }
.kpi-num.kpi-text { font-size: 1rem; font-weight: 600; }

/* ---------- پروژه‌ها ---------- */
/* A project card leads with its name and its state, then the one number that
   says how it is going. Everything else on the card is context for that. */
.project-card { display: flex; flex-direction: column; gap: 0.5rem; text-decoration: none; }
.project-card-head { display: flex; align-items: center; justify-content: space-between; gap: 0.6rem; }
.project-card-head h3 { margin: 0; }
.project-card-sub { font-size: 0.8rem; margin: 0; }
.project-card-foot {
    display: flex; flex-wrap: wrap; gap: 0.75rem;
    font-size: 0.76rem; margin-top: 0.2rem;
}
.project-sub { margin: -0.2rem 0 1rem; }

/* Equal columns that fill the row, rather than nine pills each the width of
   the name inside it. The grid is what makes the avatars line up down the
   page and the last row sit under the first instead of beside nothing. */
.member-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: var(--gap-list);
}
.member-chip {
    display: flex; align-items: center; gap: 0.55rem;
    background: var(--bg);
    border: 1px solid var(--border-soft);
    /* A card, not a pill: 999px is for something one line tall, and this
       carries two. */
    border-radius: var(--radius);
    padding: 0.45rem 0.6rem;
    font-size: 0.8rem;
    min-width: 0;
}
.member-chip .colleague-avatar { width: 30px; height: 30px; flex-shrink: 0; }
/* A name and the seat behind it, so a member is read without hovering. Both
   are held to one line: a name long enough to wrap would make its card taller
   than the rest, which is the raggedness this is fixing. */
.member-chip-text {
    display: flex; flex-direction: column; line-height: 1.35;
    min-width: 0; flex: 1;
}
.member-chip-name, .member-chip-role {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    /* Both lines begin on the card's own right edge. «start» is not enough:
       `unicode-bidi: plaintext` below gives a Latin name an LTR line of its
       own, and «start» on that line means *left* — which set the name and the
       seat under it on opposite edges, drifting apart by up to 123px. */
    text-align: right;
}
/* A Latin name in an RTL card lays itself out left to right, so it is cut at
   its far end rather than at its first letter. */
.member-chip-name { font-weight: 600; unicode-bidi: plaintext; }
.member-chip-role { font-size: 0.72rem; }

/* The project's own thread. Deliberately plainer than the chat page: it is
   read as a record of the job, not watched like a conversation. */
.project-thread { display: flex; flex-direction: column; gap: 0.7rem; margin-bottom: 1rem; }
.project-msg {
    background: var(--bg);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 0.6rem 0.8rem;
}
.project-msg.mine { background: var(--accent-soft); border-color: transparent; }
.project-msg-head { display: flex; gap: 0.6rem; font-size: 0.78rem; margin-bottom: 0.25rem; }
.project-msg-name { font-weight: 700; }
.project-msg-body { white-space: pre-wrap; line-height: 1.8; font-size: 0.9rem; }
.project-compose { display: flex; gap: 0.6rem; align-items: flex-end; }
.project-compose textarea { flex: 1; }
.folder-form { margin-top: 1rem; }

/* ---------- ایجاد وظیفه: the dialog, in the reference's shape ---------- */
/* The title carries a single underline and nothing else, so the eye starts
   where the work starts. Everything optional lives in one dashed box below
   it, folded away until wanted — the reference's row of small icons, written
   out as words because a row of unlabelled glyphs is a guessing game. */
.task-compose { max-width: 720px; }
.tc-title input {
    width: 100%;
    border: none;
    border-bottom: 2px solid var(--border);
    border-radius: 0;
    background: transparent;
    font-size: 1.05rem;
    padding: 0.5rem 0.2rem;
}
.tc-title input:focus { outline: none; border-bottom-color: var(--accent); }
.tc-title input.is-empty { border-bottom-color: var(--red); }
.tc-title input::placeholder { color: var(--muted); }

.tc-box {
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    padding: 0.9rem 1rem;
    margin: 1rem 0;
    background: var(--surface);
}
.tc-desc {
    width: 100%;
    border: none;
    border-bottom: 1px dashed var(--border);
    border-radius: 0;
    background: transparent;
    resize: vertical;
    padding: 0.4rem 0.2rem;
    margin-bottom: 0.6rem;
}
.tc-desc:focus { outline: none; border-bottom-color: var(--accent); }

.tc-rows { display: flex; flex-direction: column; }
.tc-fold { border-top: 1px solid var(--border-soft); }
.tc-fold:first-child { border-top: none; }
.tc-fold > summary {
    list-style: none;
    cursor: pointer;
    padding: 0.55rem 0.2rem;
    font-size: 0.9rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.tc-fold > summary::-webkit-details-marker { display: none; }
.tc-fold > summary:hover { color: var(--accent-ink); }
.tc-plus {
    width: 20px; height: 20px;
    border-radius: 6px;
    background: var(--accent-soft);
    color: var(--accent-ink);
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 700; line-height: 1;
    flex-shrink: 0;
}
.tc-fold-body { padding: 0.2rem 0.2rem 0.8rem; display: flex; flex-direction: column; gap: 0.5rem; }
.tc-fold-body input[type="text"] { width: 100%; }

.tc-choice { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.tc-choice input { width: auto; margin: 0; }
.tc-choice-text { display: flex; flex-direction: column; }
.tc-choice-text .muted { font-size: 0.75rem; }
.tc-choice-extra { display: flex; flex-wrap: wrap; gap: 0.9rem; padding-top: 0.4rem; }

.tc-project { display: flex; align-items: center; gap: 0.8rem; flex-wrap: wrap; }
.tc-project select { max-width: 320px; }
.tc-foot {
    display: flex; align-items: center; justify-content: flex-end; gap: 1.2rem;
    margin-top: 1.2rem;
}

/* The checklist on a task that already exists. */
.checklist { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.9rem; }
.check-row { display: flex; align-items: center; gap: 0.6rem; }
.check-row .inline-form { margin-bottom: 0; }
.check-text { flex: 1; font-size: 0.9rem; }
.check-row.is-done .check-text { text-decoration: line-through; color: var(--muted); }
.task-tags { margin: 0 0 1rem; }

/* ---------- تقویم وظایف: switches, filters, day dots ---------- */
.cal-bar { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.8rem; }
.cal-switches { display: flex; flex-wrap: wrap; gap: 1.2rem; margin-bottom: 0.8rem; }
/* A switch rather than a checkbox: it is a view being turned on, not a value
   being filled in, and the reference draws it the same way. */
.cal-switch {
    display: inline-flex; align-items: center; gap: 0.5rem;
    text-decoration: none; color: var(--text);
    font-size: 0.86rem;
}
.cal-knob {
    width: 34px; height: 19px;
    border-radius: 999px;
    background: var(--border);
    position: relative;
    transition: background 0.15s;
    flex-shrink: 0;
}
.cal-knob::after {
    content: "";
    position: absolute;
    top: 2px; inset-inline-start: 2px;
    width: 15px; height: 15px;
    border-radius: 50%;
    background: #fff;
    transition: inset-inline-start 0.15s;
}
.cal-switch.is-on .cal-knob { background: var(--accent); }
.cal-switch.is-on .cal-knob::after { inset-inline-start: 17px; }

.cal-basis { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.8rem; }
.cal-basis .tab { border-bottom-width: 2px; }
.cal-filter { margin-bottom: 1rem; }
.cal-filter > summary { cursor: pointer; list-style: none; }
.cal-filter > summary::-webkit-details-marker { display: none; }
.cal-filter-form { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: flex-end; }
.cal-filter-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.82rem; }
.cal-filter-form select { min-width: 170px; }
/* The control that answers whichever of the three the dropdown is set to.
   Only one is on the page at a time; «hidden» is what puts the others away,
   so it has to win over the flex display this row gives its children. */
.cal-filter-form .filter-value { display: flex; align-items: center; }
.cal-filter-form .filter-value[hidden] { display: none; }
.cal-filter-foot { display: flex; align-items: center; gap: 0.8rem; }

.calendar-cell.is-chosen { box-shadow: inset 0 0 0 2px var(--accent-ink); }
.calendar-daynum { text-decoration: none; }
.calendar-daynum:hover { color: var(--accent-ink); }
/* One dot a day when titles are folded away: enough to see where the work
   sits, and it stays legible at a month's zoom. */
.cal-dot {
    display: flex; align-items: center; gap: 0.3rem;
    text-decoration: none; color: var(--muted); font-size: 0.72rem;
    margin-top: auto;
}
.cal-dot-mark {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--accent);
    flex-shrink: 0;
}

/* A document that does not match its folder. Red, because it is the one thing
   on the page somebody has to act on — and stated in words underneath, since
   colour alone is not a message. */
.doc-misfiled { border-inline-start: 3px solid var(--red); padding-inline-start: 0.6rem; }
.doc-warn { display: block; font-size: 0.76rem; color: var(--red-deep); margin-top: 0.15rem; }
.line-row .inline-form select { min-width: 130px; }

/* Channels in the chat's sidebar. */
.chat-peers-sub { margin-top: 0.4rem; }
.chat-empty-note { padding: 0.5rem 0.9rem; font-size: 0.78rem; }
.chan-new { padding: 0.5rem 0.9rem 0.8rem; }
.chan-new > summary {
    list-style: none; cursor: pointer;
    color: var(--accent-ink); font-size: 0.85rem; font-weight: 600;
}
.chan-new > summary::-webkit-details-marker { display: none; }
.chan-form { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.5rem; }
.channel-post { max-width: none; }
.bubble-foot { display: flex; align-items: center; gap: 0.6rem; font-size: 0.72rem; margin-top: 0.2rem; }
.bubble-foot .inline-form { margin-bottom: 0; }
.channel-note { padding: 0.8rem; text-align: center; }

/* ---------- Moving between pages ---------- */
/* Anchors inside a page scroll rather than snap. Page-to-page navigation is
   deliberately left alone: a cross-fade was tried here and taken out again —
   while it runs, the outgoing and incoming pages are both on screen, and the
   two sets of text sitting on top of each other read as a shift and a moment
   of bolding. A plain, instant page swap is steadier than a soft one that
   doubles the words.

   The typeface is preloaded in the head for the same reason: the faces are
   «font-display: optional», so a page that has not got the file yet renders
   in the fallback for its whole life — and the difference between one page in
   Yekan Bakh and the next in Tahoma is exactly a jump and a bolding. */
html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
}

/* The checklist's own «+», under the single line it starts with. */
.check-add { display: flex; flex-direction: column; gap: 0.4rem; }
.check-plus {
    align-self: flex-start;
    width: 26px; height: 26px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--accent-ink);
    font-size: 1rem; font-weight: 700; line-height: 1;
    cursor: pointer;
}
.check-plus:hover { background: var(--accent-soft); border-color: var(--accent); }

/* Monitoring: two findings side by side above the tables. */
.mon-highlights { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 800px) { .mon-highlights { grid-template-columns: 1fr; } }

/* ---------- مانیتورینگ ---------- */
/* The counts read as a sentence — mark, word, number — rather than as five
   more cards; the cards below are for the figures that need comparing. */
.mon-counts { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; margin-bottom: 1rem; }
.mon-count { display: inline-flex; align-items: center; gap: 0.45rem; font-size: 0.9rem; }
.mon-count b { font-weight: 700; }
.mon-mark {
    width: 26px; height: 26px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    color: #fff; flex-shrink: 0;
}
.mark-blue { background: var(--accent); }
.mark-green { background: var(--green-deep); }
.mark-teal { background: #0f7c78; }
.mark-amber { background: #a8720a; }
.mark-red { background: var(--red-deep); }

.mon-facts { display: flex; flex-direction: column; gap: var(--gap-list); margin-bottom: var(--gap-block); }
.mon-fact {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 0.7rem 1rem;
}
.mon-fact-key { display: inline-flex; align-items: center; gap: 0.5rem; color: var(--muted); font-size: 0.86rem; }
.mon-fact-value { font-weight: 700; }

/* The sections, as the reference's row of coloured buttons. */
.mon-sections { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.2rem; }
.mon-section {
    padding: 0.5rem 0.9rem;
    border-radius: 999px;
    text-decoration: none;
    font-size: 0.86rem; font-weight: 600;
    color: #fff;
    opacity: 0.82;
}
.mon-section:hover, .mon-section.is-on { opacity: 1; }
.mon-section.is-on { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px currentColor; }
.tone-green-deep { background: #146c43; }
.tone-green { background: #2f7d32; }
.tone-blue { background: var(--accent); }
.tone-teal { background: #0f7c78; }
.tone-orange { background: #b5540b; }

.mon-highlights { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 800px) { .mon-highlights { grid-template-columns: 1fr; } }

/* ---------- مانیتورینگ: the reference's counts, facts and sections ---------- */
/* A coloured mark, the word, the number — read left to right as one phrase
   rather than stacked into cards, because there are five of them and they are
   context for the tables underneath, not the point of the page. */
.mon-counts {
    display: flex; flex-wrap: wrap; gap: 0.6rem 1.6rem;
    margin-bottom: 1rem;
}
.mon-count { display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.88rem; }
.mon-count b { font-weight: 700; }
.mon-mark {
    width: 28px; height: 28px;
    border-radius: 8px;
    display: inline-flex; align-items: center; justify-content: center;
    color: #fff;
    flex-shrink: 0;
}
.mark-blue { background: var(--blue); }
.mark-green { background: var(--green-deep); }
.mark-teal { background: #14605c; }
.mark-amber { background: #8a4a06; }
.mark-red { background: var(--red-deep); }

.mon-facts { display: flex; flex-direction: column; gap: var(--gap-list); margin-bottom: var(--gap-block); }
.mon-fact {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 0.7rem 1.1rem;
}
.mon-fact-key { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 600; font-size: 0.88rem; }
.mon-fact-value { color: var(--accent-ink); font-weight: 700; font-size: 0.9rem; }

.mon-sections { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 1.2rem; }
/* Each section keeps its own colour, as in the reference, and every one of
   them carries white — these are filled pills, and dark text on a filled
   colour is what made the earlier palette unreadable. */
.mon-section {
    padding: 0.5rem 1rem;
    border-radius: 999px;
    text-decoration: none;
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
    opacity: 0.9;
}
.mon-section:hover { opacity: 1; }
.mon-section.is-on { opacity: 1; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px currentColor; }
.mon-section.tone-green-deep { background: #14603b; }
.mon-section.tone-green { background: var(--green-deep); }
.mon-section.tone-blue { background: var(--accent); }
.mon-section.tone-teal { background: #14605c; }
.mon-section.tone-orange { background: #9a4a09; }

/* A note inside a panel head sits on the accent, so it is white like the rest
   of the head — «muted» there was grey on blue and barely legible. */
.head-note { font-weight: 400; font-size: 0.78rem; color: var(--muted); }

/* ---------- KPI: the drill-down and the approval queue ---------- */
/* One missed indicator, why it was missed, whose figures were missed under it,
   and what to do — read top to bottom in that order. */
.blame { padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--border-soft); }
.blame:last-child { border-bottom: none; }
.blame-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem; }
.blame-why { margin: 0.25rem 0 0.5rem; font-size: 0.84rem; }
.blame-culprits { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 0.6rem; }
.blame-action { font-size: 0.88rem; margin: 0; }
/* A suggestion the portal wrote is marked as such: the reader should be able
   to tell it from the unit's own written remedy at a glance. */
.blame-action.is-generated { color: var(--muted); }
.blame-action.is-generated b { color: var(--text); }
.unit-why-line { margin: -0.2rem 0 1rem; }
.unit-open { padding: 0 1.1rem 0.4rem; }

.pending-row {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.5rem 1rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 1px solid var(--border-soft);
}
.pending-row:last-child { border-bottom: none; }
.pending-main { display: flex; flex-direction: column; gap: 0.2rem; }
.pending-figure { display: flex; flex-direction: column; align-items: flex-end; gap: 0.2rem; font-size: 0.78rem; }
.pending-value { font-size: 1.1rem; font-weight: 700; }
.pending-actions {
    grid-column: 1 / -1;
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
}
.pending-actions input[type="text"] { flex: 1; min-width: 180px; margin: 0; }
@media (max-width: 700px) {
    .pending-row { grid-template-columns: 1fr; }
    .pending-figure { align-items: flex-start; }
}

/* ---------- Dictation: a microphone inside a text field ---------- */
/* The button sits at the far end of the box rather than beside it, so a row
   of fields keeps its alignment whether or not the browser allows recording —
   where it does not, the button is removed and the field is simply full width. */
.dictate-wrap { position: relative; display: block; }
.dictate-wrap > input,
.dictate-wrap > textarea { width: 100%; padding-inline-end: 2.2rem; }
.dictate-btn {
    position: absolute;
    inset-inline-end: 6px;
    top: 6px;
    width: 26px; height: 26px;
    border: none;
    background: transparent;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
    opacity: 0.55;
}
.dictate-btn:hover { opacity: 1; background: var(--accent-soft); }
.dictate-btn[data-state="recording"] { opacity: 1; color: var(--red-deep); }
.dictate-btn[data-state="busy"] { opacity: 1; }

/* ---------- A record's actions, as a row ---------- */
/* Download, share, replies, delete belong to one record and are read as a
   set, so they sit on one line as buttons of the same size rather than as a
   column of underlined links. The folded ones (share, replies) keep their
   arrow: they open something rather than doing something. */
.record-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
    margin: 0.8rem 0 0.2rem;
}
.record-actions .btn-sm { margin: 0; }

.meeting-share > summary,
.meeting-replies > summary,
.record-fold > summary {
    display: inline-flex; align-items: center; gap: 0.35rem;
    list-style: none; cursor: pointer;
    padding: 0.35rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--accent-ink);
    font-size: 0.8rem; font-weight: 600;
}
.meeting-share > summary::-webkit-details-marker,
.meeting-replies > summary::-webkit-details-marker,
.record-fold > summary::-webkit-details-marker { display: none; }
.meeting-share > summary::before,
.meeting-replies > summary::before,
.record-fold > summary::before { content: "▾"; font-size: 0.7rem; }
.meeting-share > summary:hover,
.meeting-replies > summary:hover,
.record-fold > summary:hover { border-color: var(--accent); background: var(--accent-soft); }

/* ---------- The channel composer ---------- */
/* One line tall to start with, growing only as far as five before it scrolls:
   a channel post is usually a sentence, and a box four lines deep pushed the
   thread off the screen to hold air. */
.chat-compose textarea {
    flex: 1; margin: 0; resize: none;
    min-height: 38px; max-height: 8.5rem;
    padding: 0.45rem 0.7rem;
    line-height: 1.6;
}
.compose-clip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 1rem;
    opacity: 0.7;
}
.compose-clip:hover { opacity: 1; background: var(--accent-soft); }
.bubble-audio { display: block; width: 100%; max-width: 320px; margin-top: 0.4rem; }
.channel-post .attachment-item { margin-top: 0.4rem; }
.btn-ghost[disabled] { opacity: 0.45; cursor: not-allowed; }

/* The company's mark in a channel header. Small and quiet: it says whose
   channel this is without competing with the channel's own name. */
.channel-logo { height: 34px; width: auto; flex-shrink: 0; }

/* ---------- صورت‌جلسات: one meeting per fold ---------- */
/* The same shape the indicators list uses, for the same reason: the page is
   read to find one meeting, and a dozen open transcripts is a wall of text. */
.meeting-summary {
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
    cursor: pointer; list-style: none;
}
.meeting-summary::-webkit-details-marker { display: none; }
.meeting-title { flex: 1; min-width: 8rem; }
.meeting-panel .unit-chevron { transition: transform 0.15s; }
.meeting-panel[open] .unit-chevron { transform: rotate(180deg); }

/* Changing the picture sits beside the picture it changes. */
.profile-info { flex: 1; min-width: 12rem; }
.profile-avatar-form {
    display: flex; flex-direction: column; gap: 0.5rem;
    align-items: flex-start;
    padding-inline-start: 1.25rem;
    border-inline-start: 1px solid var(--border-soft);
    min-width: 15rem;
}
.profile-avatar-form .btn { margin: 0; }
.profile-avatar-label { font-size: 0.85rem; font-weight: 600; }
@media (max-width: 700px) {
    .profile-avatar-form {
        border-inline-start: none; padding-inline-start: 0;
        border-top: 1px solid var(--border-soft); padding-top: 1rem; width: 100%;
    }
}

/* A channel's name reads beside the mark, not under it. */
.channel-head-text { flex: 1; min-width: 0; }

/* What the microphone is doing, under the field it is filling. */
.dictate-note { font-size: 0.76rem; margin: 0.2rem 0 0.4rem; }

/* ---------- A filed report, as a sheet ----------
   The paper form is read as ruled blocks: a heading, then question and answer
   on one line. What stood here — loose boxes with no rules — was the same
   information with none of the shape, and «خیلی بی‌هویت» was a fair word for
   it. Each block below is one section of the form. */
.sheet-block {
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    overflow: hidden;
    margin-bottom: 1rem;
}
.sheet-block:last-child { margin-bottom: 0; }
.sheet-head {
    background: var(--accent-soft);
    color: var(--accent-deep);
    font-weight: 600; font-size: 0.85rem;
    padding: 0.55rem 1rem;
    border-bottom: 1px solid var(--border-soft);
}
.sheet-block .table-wrap { border: none; }
.sheet-table { min-width: 0; }
.sheet-table tbody tr:nth-child(even) { background: #fafafc; }
.sheet-table tbody tr:hover { background: var(--accent-soft); }
/* The question column: a third of the width, so answers line up down the page
   instead of starting wherever the question happened to end. */
.sheet-answers th[scope="row"] {
    width: 38%; background: transparent; color: var(--text);
    font-weight: 500; vertical-align: top;
}
.sheet-answers td { vertical-align: top; }
/* Figures read against a common right edge, in the digits of the language. */
.num-cell { font-variant-numeric: tabular-nums; white-space: nowrap; }
.sheet-table .grid-num { width: 2.5rem; color: var(--muted); text-align: center; }

/* A box that has become compulsory because of the answer above it. */
.field-needed { border-color: var(--accent-ink); }

/* A second row of tabs under the first: the same control, quieter, so the
   page reads as «this period → whose reports» rather than as two equal
   choices competing for the eye. */
.tabs-sub {
    border-bottom: none;
    margin-top: -0.9rem;
    margin-bottom: 1rem;
}
.tabs-sub .tab { font-size: 0.84rem; padding: 0.35rem 0.8rem; }
.tabs-sub .tab.active {
    background: var(--accent-soft);
    color: var(--accent-deep);
    border-radius: 999px;
    border-bottom-color: transparent;
}

/* ---------- برنامه‌ریزی اجرایی پروژه ----------
   The office's own sheet: five ruled columns and a box of remarks under them.
   The day and the date are narrow and never wrap, so the eye runs down them
   the way it does on paper. */
.plan-table .plan-day { width: 5.5rem; }
.plan-table .plan-date { width: 8.5rem; }
.plan-table .plan-actions { width: 9rem; }
.plan-table td { vertical-align: top; }
.plan-table .badge { margin-inline-start: 0.4rem; }
.plan-note {
    border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm);
    margin-top: 1rem;
    overflow: hidden;
}
.plan-note-head {
    background: var(--accent-soft);
    color: var(--accent-deep);
    font-weight: 600; font-size: 0.85rem;
    padding: 0.55rem 1rem;
    border-bottom: 1px solid var(--border-soft);
    text-align: center;
}
.plan-note form, .plan-note-body { padding: 0.9rem 1rem; margin: 0; }
.plan-note textarea { width: 100%; }
.plan-note-body { white-space: pre-wrap; min-height: 4rem; }

/* The day is printed on the row, as on the paper: a fixed label, not a
   control. It reads as part of the sheet rather than as something to change. */
.plan-day-name { font-weight: 600; white-space: nowrap; }
/* «خرید دارد» — the box and its words on one line, the box first. */
.plan-buy {
    display: inline-flex; align-items: center; gap: 0.35rem;
    margin-top: 0.4rem; font-size: 0.8rem; white-space: nowrap;
}
.plan-buy input[type="checkbox"] { width: 1rem; height: 1rem; margin: 0; flex-shrink: 0; }

/* The week each page of the sheet covers, written across the table. */
.plan-week td {
    background: var(--bg);
    font-size: 0.8rem; font-weight: 600; color: var(--muted);
    text-align: center;
}

/* ---------- The planning sheet, as a page ----------
   One week to a view: a ruled grid with the day printed on each row, the way
   the paper reads. The columns are given their share of the width so the work
   column is the wide one and the day and date never wrap. */
.plan-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.plan-week-label { font-size: 0.82rem; font-weight: 500; opacity: 0.9; }
.plan-table { table-layout: fixed; min-width: 720px; }
.plan-table th { text-align: center; }
.plan-table .plan-day { width: 6rem; text-align: center; }
.plan-table .plan-date { width: 9.5rem; }
.plan-table .plan-actions { width: 8rem; }
.plan-table tbody tr:nth-child(even) { background: #fafafc; }
.plan-table td { vertical-align: top; padding: 0.55rem 0.7rem; }
.plan-table textarea, .plan-table input[type="text"] { width: 100%; }
.plan-table textarea { resize: vertical; min-height: 2.6rem; }
.plan-table .jalali-picker { width: 100%; }
.plan-table .dictate-wrap { width: 100%; }
.plan-add { flex-wrap: wrap; gap: 0.5rem; align-items: center; }
/* The body's own padding insets these now; only the room underneath the last
   row of controls is the sheet's own business. */
.plan-sheet .panel-body > .record-actions:last-child { margin-block-end: 1rem; }

/* The paper's own bands: the title across the top, and «توضیحات تکمیلی»
   across the bottom with one deep box under it. Both sit inside the frame,
   as they do on the sheet this is a copy of. */
.plan-title-row th {
    text-align: center; font-weight: 700; font-size: 0.95rem;
    padding: 0.6rem 1rem; background: var(--surface); color: var(--text);
}
.plan-title-name { border-bottom: 1px dotted var(--muted); padding: 0 2rem; }
.plan-note-row th {
    text-align: center; font-weight: 700;
    padding: 0.5rem 1rem; background: var(--surface); color: var(--text);
}
.plan-note-box td { padding: 0.7rem 1rem; }
.plan-note-box textarea { width: 100%; }
.plan-note-body { white-space: pre-wrap; margin: 0; min-height: 6rem; }
.plan-sheet .panel-body > .table-wrap { margin-block-end: 0.9rem; }

/* On paper: the sheet alone, ruled, with nothing of the portal around it. */
@media print {
    .topbar, .sidebar, .back-link, .record-actions, .plan-add, .tabs,
    .schedule-rule, .dictate-btn, .plan-actions { display: none !important; }
    .content { margin: 0; padding: 0; }
    .panel { border: none; box-shadow: none; }
    .panel-head { background: none !important; color: #000 !important;
                  border-bottom: 2px solid #000; }
    .plan-table, .plan-table th, .plan-table td { border: 1px solid #000; }
    .plan-table { border-collapse: collapse; min-width: 0; width: 100%; }
    .plan-table textarea, .plan-table input { border: none; padding: 0; }
    .plan-note { border: 1px solid #000; }
}

/* The row somebody is working on, kept under the eye on a long sheet. */
.plan-table tbody tr.is-picked,
.plan-table tbody tr.is-picked:nth-child(even) { background: #eeeef2; }

/* A notice whose record has been removed: still readable, plainly spent. */
.notif-row.is-gone .line-title { color: var(--muted); }

/* A box's own count, quieter than the unread badge beside it. */
.tab-badge-quiet { background: var(--bg); color: var(--muted); font-weight: 600; }

/* ---------- A letter, as a letter ----------
   Who wrote it, to whom, and when — above the text, the way it reads on paper.
   The sheet is one card so the letter is the thing on screen rather than a
   heading with a row of details under it. */
.letter-sheet {
    background: var(--surface);
    border: 1px solid var(--border-soft);
    border-radius: var(--radius);
    padding: 1.25rem 1.4rem 1.4rem;
    margin-bottom: 1.25rem;
}
.letter-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
    padding-bottom: 0.9rem; margin-bottom: 1rem;
    border-bottom: 1px solid var(--border-soft);
}
.letter-head-who { display: flex; align-items: center; gap: 0.7rem; min-width: 0; }
.letter-head-avatar {
    width: 42px; height: 42px; border-radius: 50%; object-fit: cover; flex-shrink: 0;
}
.letter-head-line { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: baseline; }
.letter-head-sender { font-weight: 700; }
.letter-head-to { font-weight: 500; }
.letter-head-when { font-size: 0.8rem; margin-top: 0.15rem; }
.letter-head-marks { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.letter-subject-line { font-size: 1.15rem; margin: 0 0 0.9rem; }
.letter-text { line-height: 2; white-space: pre-wrap; }
.letter-files {
    display: flex; flex-wrap: wrap; gap: 0.5rem;
    margin-top: 1.1rem; padding-top: 0.9rem;
    border-top: 1px solid var(--border-soft);
}

/* The task a letter carries, drawn as the task card is drawn elsewhere. */
.task-card { display: flex; flex-direction: column; gap: 0.6rem; }
.task-card .compose-inline { display: flex; flex-direction: column; gap: 0.3rem; }
.task-card select[multiple] { min-height: 6rem; }

/* «تحلیل گزارش» — the resolutions read out of a minute, before they are filed. */
.analyse-table .analyse-take { width: 3.5rem; text-align: center; }
.analyse-table .analyse-due { width: 12rem; }
.analyse-table td { vertical-align: top; }
.analyse-table input[type="text"], .analyse-table select { width: 100%; }

/* «فرم‌های عمومی» and «فرم‌های من» — the two halves of the forms page. */
.forms-section { font-size: 1rem; margin: 1.5rem 0 0.6rem; }
.forms-section:first-of-type { margin-top: 0.5rem; }

/* Uploading a signature, under the pad it is an alternative to. */
.sig-upload {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
    margin-top: 0.9rem; padding-top: 0.9rem;
    border-top: 1px solid var(--border-soft);
}

/* «انجام شد» — the green line after a save. It sits above the page's own
   heading, where the eye already is after pressing a button. */
.saved-note {
    display: flex; align-items: center; gap: 0.45rem;
    margin: 0 0 1rem;
    padding: 0.6rem 0.9rem;
    border-radius: 10px;
    background: #e6f4ec;
    color: var(--green-deep);
    font-size: 0.85rem;
    font-weight: 600;
    animation: saved-fade 6s forwards;
}
@keyframes saved-fade {
    0%, 70% { opacity: 1; }
    100% { opacity: 0; visibility: hidden; }
}
@media (prefers-reduced-motion: reduce) {
    .saved-note { animation: none; }
}

/* The paperclip that marks a file, wherever one is listed or offered. */
.attachment-item .icn,
.letter-clip .icn,
.file-clip .icn { vertical-align: -0.18em; flex-shrink: 0; }

/* «برچسب‌های بالای هر نوشته در یک ردیف قرار بگیرن»: a row of labels stays a
   row. Wrapping put the last one or two on a line of their own under the
   text, which read as a second, emptier row rather than as part of the same
   set. They scroll sideways instead when there are more than fit — the bar
   itself is hidden, since the row is short and dragging it is the point. */
.letter-head-marks,
.letter-tags,
.tag-list {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
}
/* The picker is the one exception, and for the same reason the rule exists:
   «برچسب‌ها در همه بخش‌ها کامل نشون داده بشه». It is not a row of labels on a
   thing — it is the list of every label there is, and a choice that is off the
   edge cannot be made. So it wraps rather than hiding its last chip; the rows
   it fills are still each one line, and every chip is visible. */
.label-picker-list { flex-wrap: wrap; }
.letter-head-marks::-webkit-scrollbar,
.letter-tags::-webkit-scrollbar,
.tag-list::-webkit-scrollbar { display: none; }
/* Nothing in such a row may be squeezed thinner than its own words. */
.letter-head-marks > *,
.letter-tags > *,
.tag-list > *,
.label-picker-list > * { flex-shrink: 0; }

/* ---------- سند جامع پروژه ----------
   The company's control document, drawn as the sheet draws it: a numbered
   block per unit, and inside each a line per milestone with its date at the
   end. Laid out as a grid so every date starts in the same column however
   long the line beside it runs. */
.master-head { display: flex; align-items: baseline; gap: 0.7rem; }
.master-head-en { font-size: 0.72rem; opacity: 0.85; letter-spacing: 0.06em; }

.master-contract { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1.1rem; }
.master-section { margin-bottom: 1.2rem; }
.master-section:last-of-type { margin-bottom: 0.4rem; }
.master-section-head {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.55rem;
    padding-bottom: 0.35rem; margin-bottom: 0.5rem;
    border-bottom: 1px solid var(--border-soft);
}
.master-number {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.9rem; padding: 0.1rem 0.4rem;
    background: var(--accent); color: var(--on-accent);
    border-radius: var(--radius-sm); font-weight: 700; font-size: 0.8rem;
}
.master-section-title { font-weight: 700; }
.master-section-en { font-size: 0.7rem; color: var(--muted); letter-spacing: 0.06em; }

.master-line {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: center; gap: 0.6rem 1rem;
    padding: 0.3rem 0;
}
.master-line-name { display: flex; flex-direction: column; min-width: 0; }
.master-line-hint { font-size: 0.75rem; }
.master-line-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
.master-line input[type="text"] { margin: 0; min-width: 12rem; }
@media (max-width: 620px) {
    .master-line { grid-template-columns: 1fr; }
}

/* A figure that leads somewhere says so under the cursor. */
a.mon-count, a.stat { text-decoration: none; color: inherit; }
a.mon-count:hover, a.stat:hover { border-color: var(--accent); }

/* A name and the seat behind it, wherever a letter names its sender. */
.letter-sender-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.letter-sender-role, .letter-head-role { font-size: 0.72rem; }
.letter-head-role { display: block; }

/* The pane a conversation tab opens when it is a place to choose from rather
   than a thread of its own. */
.chat-pick { padding: 2rem 1.6rem; display: flex; flex-direction: column; gap: 0.5rem; }
.chat-pick-mark { color: var(--accent); }
.chat-pick-name { margin: 0.3rem 0 0; font-size: 1.1rem; }
.chat-pick-list { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 1rem; }
.chat-pick-row {
    display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
    padding: 0.65rem 0.9rem; border: 1px solid var(--border-soft);
    border-radius: var(--radius-sm); text-decoration: none; color: inherit;
    background: var(--surface);
}
.chat-pick-row:hover { border-color: var(--accent); background: var(--accent-soft); }
.chat-pick-row-name { font-weight: 600; }

/* A link written on a panel's blue band takes the band's own lettering: white,
   like every other word on a blue ground in the portal. */
.panel-head .head-link,
.panel-head .row-link,
.panel-head a { color: var(--accent-ink); font-weight: 600; }
.panel-head .head-link:hover { text-decoration: underline; }

/* A project's figures lead to the tab they were counted from. */
a.kpi { text-decoration: none; color: inherit; }
a.kpi:hover { border-color: var(--accent); }

/* A filter row: every control the same width and the same height, sitting on
   one line — «دکمه و ۲ فیلد همه یک اندازه و در یک خط باشن». */
.filter-row {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.8rem;
}
.filter-row .filter-field {
    display: flex; flex-direction: column; gap: 0.3rem;
    flex: 1 1 12rem; min-width: 11rem;
}
.filter-row .filter-label { font-size: 0.82rem; color: var(--muted); }
/* A field whose caption is a «label» inherits the form's own label margin and
   so starts a few pixels lower than the ones beside it. In a filter row every
   caption is the same caption. */
.filter-row label.filter-field,
.filter-row .filter-field > .filter-label,
.filter-row label { margin: 0; }
.filter-row .filter-field { justify-content: flex-end; }
.filter-row select,
.filter-row input[type="text"],
.filter-row .btn,
.filter-row .jalali-picker .jp-display { width: 100%; margin: 0; }
.filter-row .jalali-picker { width: 100%; }
.filter-row .btn { justify-content: center; }

/* The value control of a filter row fills its field, whichever of the three
   kinds is showing. */
.filter-row .filter-value { display: block; }
.filter-row .filter-value[hidden] { display: none; }
.filter-row .filter-clear { display: flex; align-items: center; }

/* ---------- One height for every control ----------
   «در کل پلتفرم منظم بودن و در یک راستا بودن نوشته‌ها، کارت‌ها، دکمه‌ها مهمه»:
   a field, a menu, a date picker and a button are the same height wherever
   they stand together, so a row of them reads as one row. */
.btn,
.jalali-picker .jp-display,
form input:not([type="checkbox"]):not([type="radio"]),
form select,
form textarea {
    min-height: 2.6rem;
    box-sizing: border-box;
}

/* A tick box, at the size a tick box is.
 *
 * «تیک باکس خیلی بزرگه و دفرمه شده» — it was being given the width and the
 * height of a text field by the two rules above, which did not think to
 * exclude it. Measured at 1015 x 42 px on «ارسال برای واحدها». Named here
 * rather than left to the browser so every one of them in the portal is the
 * same size, which is the other half of the complaint.
 */
form input[type="checkbox"],
form input[type="radio"],
input[type="checkbox"],
input[type="radio"] {
    width: 1.05rem;
    height: 1.05rem;
    min-height: 0;
    padding: 0;
    margin: 0;
    flex: 0 0 auto;
    accent-color: var(--accent-ink);
    cursor: pointer;
}
/* And the same height, not merely the same minimum: a select, a box and a
   button each add their own padding to that floor and so came out 45, 44 and
   38 pixels tall side by side. Single-line controls are told the height. */
.btn,
.jalali-picker .jp-display,
form select,
form input:not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
    height: 2.6rem;
    line-height: 1.2;
}
/* The two that are meant to grow. */
form textarea { height: auto; }
form input[type="file"] { height: auto; }
/* A picker is a button pretending to be a field: it takes the field's own
   frame so it cannot look like a different kind of control. */
.jalali-picker .jp-display {
    width: 100%;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.9rem;
    text-align: start;
}
.jalali-picker { display: flex; align-items: stretch; gap: 0.35rem; }
.jalali-picker .jp-clear { flex-shrink: 0; }
/* A textarea is the one control that is meant to be taller. */
form textarea { min-height: 5rem; }

/* The two ways into the SOP shelf are the same width, one under the other. */
.sop-add-grid { display: flex; flex-direction: column; gap: 1.25rem; max-width: 560px; }
.sop-add-grid > .panel,
.sop-add-grid > .form-box { width: 100%; max-width: none; margin: 0; }

/* «+ ایجاد گروه» closes the strip of conversations. */
.tab-add { color: var(--accent-ink); font-weight: 600; }
.chat-head-open { margin-inline-start: auto; }


/* ---------- No card inside a card ---------- */
/* A form-box is a card: border, shadow, its own ground. Dropped into a panel
   body it drew a second card inside the first — «برچسب‌ها داخل یک کارت در یک
   کارت بزرگ نباشه، فقط در یک کارت باشه». Inside a panel it keeps its layout
   and gives up its frame. */
.panel-body > .form-box,
.panel-body > form.form-box {
    background: none;
    border: none;
    box-shadow: none;
    border-radius: 0;
    padding: 0.6rem 0;
    max-width: none;
}

/* ---------- The strip over the conversation column ---------- */
/* The tabs on one side and the one button that makes something on the other,
   so the button is never pushed off the end by a long row of tabs. */
.tab-strip {
    display: flex;
    align-items: center;
    gap: 1rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1.1rem;
}
.tab-strip .tabs { flex: 1; min-width: 0; border-bottom: none; margin-bottom: 0; }
.tab-strip-action { flex-shrink: 0; gap: 0.4rem; margin-block-end: 0.4rem; }
.chat-pick-note { max-width: 34rem; line-height: 1.9; }


/* ---------- Making a group where you are standing ---------- */
/* `display: flex` on a class beats the browser's own `[hidden]` rule, so the
   form stood open before anybody asked for it. Said here explicitly. */
.group-new[hidden] { display: none; }
.group-new {
    display: flex; flex-direction: column; gap: 0.5rem;
    padding: 0.7rem 1rem 0.9rem;
    border-bottom: 1px solid var(--border-soft);
    background: var(--bg);
}
.group-new-people {
    display: flex; flex-direction: column; gap: 0.1rem;
    max-height: 11rem; overflow-y: auto; scrollbar-width: thin;
}
.group-new-foot { display: flex; align-items: center; gap: 0.8rem; }
.group-new-foot .row-link {
    background: none; border: none; font: inherit; cursor: pointer;
    color: var(--muted);
}
/* Taking something away is written in the colour of taking something away. */
.row-link-danger {
    background: none; border: none; font: inherit; cursor: pointer;
    color: var(--red-deep); font-weight: 600;
}
.chat-head-delete { margin: 0; }


/* ---------- A mark the portal writes ---------- */
/* Not an input: nobody types an approval into a table cell. It is blank until
   the seat it belongs to has signed, and then it is that seat's word. */
.cell-stamp {
    display: inline-block;
    font-size: 0.78rem;
    color: var(--muted);
    white-space: nowrap;
}
.cell-stamp.is-on {
    color: var(--green-deep);
    font-weight: 600;
}


/* =======================================================================
   موبایل و تبلت
   =======================================================================

   Everything below was written against measurements taken in a 375×812 and a
   768×1024 window, not against a guess at what a phone is like. What the
   measurements said, before any of it:

     · 444px of colleague list above the page, on 9 of 31 pages
     · 8 tables between 620px and 870px wide, inside a 375px screen
     · 272 things to tap under 40px, the smallest of them 14px
     · 268 pieces of text under 12px
     · تقویم وظایف shifted 40px sideways, its right edge cut off

   Every block below says «screen», and that word is doing work: a sheet of
   paper has no pointer, so a printed page reports «hover: none» and would
   otherwise have collected the 44px tap targets and the 16px fields meant for
   a thumb. How the portal prints is already settled, further down, in its own
   «@media print».

   ----------------------------------------------------------------------- */

/* ---------- A table, read as cards ---------------------------------------

   A row of a wide table on a phone is something you drag sideways, losing the
   header the moment you move — so by the third column you no longer know what
   you are reading. Below 700px each row becomes its own small card and every
   value carries its column's name beside it. Those names are put on the cells
   by responsive.js, which reads them from the table's own header, so this
   works for every table in the portal without touching a single template. */
@media screen and (max-width: 700px) {
    .table-wrap {
        overflow-x: visible;
        background: none;
        border: none;
        box-shadow: none;
    }

    /* The 620px floor is what keeps columns from crushing each other in a
       real table. A stack of cards has no columns to crush, and the floor is
       what was making the page 620px wide on a 375px screen. */
    .table, .plan-table, .sheet-table { min-width: 0; }

    /* The header is no longer drawn, but it is still read aloud, and it is
       still the source of every label below. */
    .table thead {
        position: absolute;
        width: 1px; height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .table, .table tbody, .table tr, .table td { display: block; width: auto; }

    .table tr {
        background: var(--surface);
        border: 1px solid var(--border-soft);
        border-radius: var(--radius-lg);
        box-shadow: var(--shadow);
        margin-block-end: 0.7rem;
        padding: 0.25rem 0.9rem;
    }

    .table td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 1rem;
        /* Centred is right for a column of figures standing under a heading;
           in a card the value belongs at the end of its own line, opposite
           the name of the thing it is. */
        text-align: start;
        /* nowrap kept the columns of a table honest. A card has the width. */
        white-space: normal;
        border: none;
        border-block-end: 1px solid var(--border-soft);
        padding: 0.55rem 0;
    }
    .table td:last-child { border-block-end: none; }

    .table td[data-label]::before {
        content: attr(data-label);
        font-weight: 700;
        color: var(--muted);
        font-size: 0.8rem;
        flex: 0 0 auto;
        text-align: start;
    }

    /* A cell spanning the table is a sentence — «نامه‌ای برای نمایش وجود
       ندارد» — not a value with a name. */
    .table td[colspan] {
        display: block;
        text-align: center;
        color: var(--muted);
    }
    .table tr:has(td[colspan]) {
        box-shadow: none;
        background: none;
        border-style: dashed;
    }

    /* An empty cell, in a card, is a blank line that says nothing. */
    .table td:empty { display: none; }
}

/* ---------- Room enough for a finger ------------------------------------

   44px is about what a fingertip actually covers. The controls named here
   measured between 14px and 39px. The calendar's day numbers are given a
   little less than the rest: thirty-one of them share one screen, and at 44
   the month would be taller than the window.

   Asked for by the screen being small OR by there being no mouse: a tablet is
   768px wide and every one of these is still pressed with a thumb. A laptop at
   the same width has a pointer, and a pointer does not need the room. */
@media screen and (max-width: 700px),
       screen and (hover: none) and (max-width: 1024px) {
    .topbar-toggle,
    .btn-icon {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    .back-link,
    .tab,
    .work-link,
    .cal-switch,
    .mon-section,
    .mon-count {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
    }

    .row-link,
    .link-btn,
    .chip,
    .line-title {
        min-height: 40px;
        display: inline-flex;
        align-items: center;
    }

    /* Measured at 26px and 38px: the tick that adds a checklist line, and the
       microphone. Both are pressed while holding the phone one-handed. */
    .check-plus,
    .voice-btn {
        min-width: 40px;
        min-height: 40px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Two actions on one card line, kept apart enough to tell them apart. */
    .table td .row-link + .row-link { margin-inline-start: 0.9rem; }

    .calendar-daynum,
    .calendar-add {
        min-width: 34px;
        min-height: 34px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* iOS zooms the whole page in when a focused field's text is under 16px,
       and it never zooms back out by itself — which is how a form ends up
       permanently too wide to read. Measured on فرم ریز هزینه تنخواه: 13.76px,
       because «form input» and «.field-cell input» each name the field more
       precisely than a bare «input» does. The :not() pair carries the weight
       needed to win without changing anything else about the control; ticks
       and circles are left alone, since nothing is typed into those. */
    input:not([type="checkbox"]):not([type="radio"]),
    select,
    textarea,
    /* Named outright, because these are the rules being overridden and a
       reader should be able to see which. */
    form input, form select, form textarea,
    .field-cell input, .field-cell select, .field-cell textarea,
    .filter-bar select, .filter-row select, .letter-filter select,
    .dictate-wrap > input, .dictate-wrap > textarea { font-size: 16px; }
}

/* ---------- Text a phone can read --------------------------------------

   Eleven and a half pixels is fine on a screen at desk distance and small on
   one held at arm's length. These are the classes the sweep found under
   twelve; nothing else is touched, so the page keeps its proportions.

   To 1024 rather than 700: a tablet is held further away than a phone, not
   closer, so if the text was too small for one it is too small for the other.
   A laptop at this width gains a little as well, and loses nothing. */
@media screen and (max-width: 1024px) {
    .colleague-sub,
    .chat-peer-sub,
    .org-count,
    .software-desc { font-size: 0.8rem; }

    .badge,
    .side-badge,
    .chip,
    .tag-chip,
    .cell-stamp { font-size: 0.78rem; }

    .chart-lbl { font-size: 0.75rem; }

    /* A dense axis writes its months on their side, and at 0.63rem they were
       10px. Matched specificity, or the rule above loses to the one that set
       them. Turned further over as well: a steeper label needs less width, so
       it can be larger without touching its neighbour. Thirty columns on a
       375px screen is still a chart to turn the phone sideways for. */
    .chart-axis.is-dense .chart-lbl { font-size: 0.78rem; }
    .chart-axis.is-dense .chart-val { font-size: 0.78rem; }

    /* «نمودار رو هم روی گوشی اسکرول افقی کن». Thirty columns on a 375px screen
       is 12px each — narrower than one digit — so instead of shrinking the
       chart to fit the screen, the chart keeps a readable column and the
       screen moves along it.

       2.9rem a column: enough for a turned month name to stand clear of the
       one beside it. Asked for as a minimum, so a chart of seven columns is
       still simply the width of the panel and does not scroll at all. Being
       a right-to-left page it would open on the oldest end, so responsive.js
       nudges it to today. */
    .chart-scroll.is-dense {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        /* Room underneath for the turned labels, which hang below the axis. */
        padding-bottom: 0.2rem;
    }
    .chart-scroll.is-dense > .chart { min-width: calc(var(--cols) * 2.9rem); }

    /* A thin rail, so it is visible that there is more chart to the side. */
    .chart-scroll.is-dense::-webkit-scrollbar { height: 4px; }
    .chart-scroll.is-dense::-webkit-scrollbar-thumb {
        background: var(--border);
        border-radius: 999px;
    }
}

/* ---------- The desk's colleague list, folded ---------------------------

   Open on a wide screen, exactly as it was. On a phone it stacked above the
   page — 444px of names between the reader and their own کارتابل — so there it
   starts folded. It is a shortcut into a conversation, not the page itself. */
.side-fold > .side-panel-head {
    cursor: default;
    list-style: none;
}
.side-fold > .side-panel-head::-webkit-details-marker { display: none; }
.side-fold > .side-panel-head::marker { content: ""; }

@media screen and (max-width: 700px) {
    .side-fold > .side-panel-head {
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 44px;
    }
    /* A chevron that turns over, so it is plain the list is there and plain
       which way it is about to go. */
    .side-fold > .side-panel-head::after {
        content: "";
        width: 0.5rem; height: 0.5rem;
        /* Physical sides, not logical ones. «border-inline-start» is the right
           edge in a right-to-left page, and the rotated corner then pointed
           sideways instead of down — measured on کارتابل, where it read as
           «go somewhere» rather than «open this». */
        border-left: 2px solid var(--muted);
        border-bottom: 2px solid var(--muted);
        transform: rotate(-45deg);
        transition: transform 0.15s;
        margin-inline-end: 0.2rem;
    }
    .side-fold[open] > .side-panel-head::after { transform: rotate(135deg); }

    /* Even opened, a list to glance down — not one to scroll the page by. */
    .side-fold > .side-panel-body {
        max-height: 50vh;
        overflow-y: auto;
    }
}

@media (prefers-reduced-motion: reduce) {
    .side-fold > .side-panel-head::after { transition: none; }
}

/* ---------- The page's own furniture on a phone ------------------------- */
@media screen and (max-width: 700px) {
    /* A row of tabs wider than the screen scrolls sideways on its own. Without
       this the page was doing the scrolling instead. */
    .tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .tabs::-webkit-scrollbar { height: 0; }

    /* Side by side on one row, a heading and its buttons each got a third of
       the screen: the title wrapped to two lines and the buttons sat left of
       it looking like they belonged to something else. The title goes first,
       the actions under it, each the width of the page. */
    .page-head,
    .page-header {
        flex-direction: column;
        align-items: stretch;
        gap: 0.7rem;
    }
    .head-actions { width: 100%; }
    .head-actions .btn,
    .head-actions > form > .btn {
        flex: 1 1 10rem;
        justify-content: center;
    }

    /* Four filter controls across a phone leaves each too narrow to say what
       it filters: «همه‌ی ذ» where the word is «همه‌ی ذینفعان». Each one gets a
       readable width and the row wraps instead. */
    .filter-row,
    .filter-bar,
    .letter-filter,
    .cal-filter-form { flex-wrap: wrap; }
    .filter-row .filter-field,
    .filter-bar .filter-field,
    .letter-filter .filter-field { flex: 1 1 10rem; min-width: 0; }
    .filter-row select, .filter-row input[type="text"],
    .filter-bar select, .filter-bar input[type="text"],
    .letter-filter select, .letter-filter input[type="text"] {
        width: 100%;
        min-width: 0;
    }
}


/* ---------- The month, made readable -----------------------------------

   «در تقویم وظایف رنگ هایلایت روی نوشته‌ها و سایز فونت تغییر کنه اصلا ظاهر
   مناسبی نداره».

   Each task used to be a solid block of saturated colour — red, orange,
   green, purple — carrying white lettering at 11.5px. Five of them in a
   week turned the month into a paint chart, and the writing on top was the
   smallest text on the page.

   Now the colour is a bar at the edge of a pale tint, the words are ordinary
   dark text at 12.8px, and the priority is still readable at a glance without
   shouting. The same information, a quieter month. */
.calendar-task {
    font-size: 0.8rem;
    line-height: 1.45;
    padding: 0.2rem 0.45rem;
    border-radius: 6px;
    /* The tint and the bar both come from one colour per priority, set below,
       so a new priority needs one line rather than four rules. */
    background: var(--cal-tint, var(--bg));
    color: var(--text);
    border-inline-start: 3px solid var(--cal-edge, var(--muted));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
    display: block;
    cursor: grab;
}
.calendar-task:hover {
    background: var(--cal-edge, var(--muted));
    color: #fff;
}

/* «اولویت اول» through «OKR», each one colour. */
.calendar-task.priority-first         { --cal-edge: var(--red-deep);   --cal-tint: #fdeceb; }
.calendar-task.priority-essential     { --cal-edge: #b45309;           --cal-tint: #fdf3e6; }
.calendar-task.priority-non_essential { --cal-edge: #6a6a74;           --cal-tint: #f2f2f5; }
.calendar-task.priority-okr           { --cal-edge: #6d28d9;           --cal-tint: #f2ecfd; }
/* Older names, still on rows filed before the four above settled. */
.calendar-task.priority-important     { --cal-edge: #b45309;           --cal-tint: #fdf3e6; }
.calendar-task.priority-urgent        { --cal-edge: var(--red-deep);   --cal-tint: #fdeceb; }

.calendar-task-done {
    text-decoration: line-through;
    opacity: 0.55;
}
.calendar-task-tick { margin-inline-end: 0.2rem; font-weight: 700; }

/* A cell has to be able to hold three or four of these and still be a cell. */
.calendar-cell { min-height: 108px; }
.calendar-daynum { font-size: 0.84rem; }

/* ---------- Dragged from one day to another ----------------------------

   The card being carried, and the day it is over. Deliberately quiet: a drag
   that lights up the whole grid makes it harder to see where you are, not
   easier. */
.calendar-task.is-lifted { opacity: 0.4; cursor: grabbing; }
.calendar-cell.is-target {
    box-shadow: inset 0 0 0 2px var(--accent);
    background: var(--accent-soft);
}
/* A day nobody may drop on — the drag is refused before it starts, rather
   than after the request comes back. */
.calendar-cell.is-refused { cursor: no-drop; }

/* Said once, under the grid, rather than as a tooltip nobody opens. */
.calendar-hint {
    font-size: 0.8rem;
    color: var(--muted);
    margin: 0.5rem 0 0;
}

@media (max-width: 700px) {
    /* Dragging with a finger on a month this size is a poor way to move
       anything, so the hint does not promise it there. */
    .calendar-hint-drag { display: none; }
    .calendar-task { font-size: 0.78rem; }
}

/* The filter row: the controls, and the labels menu beside them.
   «تب برچسب‌ها در نامه‌ها در قسمت فیلتر قرار بگیره» — it used to sit among the
   boxes, where it read as a fifth box rather than as what it is. */
.letter-filter-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin-bottom: 0.9rem;
}
.letter-filter-row > .letter-filter { margin-bottom: 0; flex: 1 1 22rem; }
.letter-filter-row > .label-menu { flex: 0 0 auto; }
/* No longer a tab, so it stops borrowing the tab's underline and spacing. */
.letter-filter-row .label-menu > .tab {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm, 8px);
    padding: 0.42rem 0.8rem;
    background: var(--surface);
}

@media (max-width: 700px) {
    .letter-filter-row > .letter-filter { flex: 1 1 100%; }
}

/* A text box grows downwards, not sideways.
 *
 * «فیلد خاکستری از کارت سفید خارج میشه» — a textarea is resizable in both
 * directions by default, and the grip in its corner is an invitation. One
 * drag on «افزودن پیام» put it 365px past the edge of its own white card,
 * measured: a 560px card holding a 900px field.
 *
 * Vertical is the direction that is actually wanted — a long message needs
 * more lines, not a wider line — and it cannot break the layout. The
 * max-width is the belt to that braces: even a width set some other way stops
 * at the box it lives in.
 */
textarea {
    resize: vertical;
    max-width: 100%;
}

/* ---------- A unit's own mark -------------------------------------------

   Instead of twelve upload forms asking for photographs nobody had. The tone
   is the unit's, the shape says what it does, and both come from the set the
   portal already draws — so a unit looks the same here as it does anywhere
   else, with nothing to keep up to date. */
.unit-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 13px;
    flex: 0 0 auto;
}
.unit-mark-blue   { background: #e8ecfd; color: #3a34bd; }
.unit-mark-green  { background: #e4f6ec; color: #1a7f4b; }
.unit-mark-orange { background: #fdf0e2; color: #b45309; }
.unit-mark-violet { background: #f1ebfd; color: #6d28d9; }

/* The whole card is the way in, so the cursor and the hover say so. */
.unit-card-link {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.45rem;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.unit-card-link:hover {
    border-color: var(--accent);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.07);
}
.unit-card-link h3 { margin: 0; }
.unit-card-go {
    margin-top: auto;
    font-size: 0.82rem;
    color: var(--accent-ink);
    font-weight: 600;
}

.unit-head { display: flex; align-items: center; gap: 0.7rem; }
.unit-head h1 { margin: 0; }

/* ---------- The chart, drawn properly ----------------------------------

   «ظاهر نمودار چارت سازمانی رو از نظر گرافیکی حرفه‌ای و زیبا کن».

   What it was: one flat white box per person, every box identical, the whole
   chart a uniform grey. Nothing about it said who was above whom except the
   lines, so reading it meant tracing lines.

   What it is now: a face or an initial to find somebody by, a weight that
   drops with each level so the shape reads before the names do, and a colour
   only at the top. That last part is the portal's rule — colour belongs to
   the header — and a chart that obeyed it turned out to be calmer as well. */
.org-card {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 0.85rem 1rem 0.75rem;
    border: 1px solid var(--border-soft);
    border-radius: 14px;
    background: var(--surface);
    min-width: 186px;
    max-width: 232px;
    text-align: center;
    margin-inline: auto;
    box-shadow: 0 1px 2px rgba(16, 18, 34, 0.05);
    transition: box-shadow 0.16s, transform 0.16s, border-color 0.16s;
}
.org-card:hover {
    box-shadow: 0 8px 22px rgba(16, 18, 34, 0.1);
    transform: translateY(-1px);
    border-color: var(--border);
}

/* A face to find somebody by, or the letter their name starts with. */
.org-avatar {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    color: var(--muted);
    font-weight: 700;
    font-size: 0.95rem;
    overflow: hidden;
    margin-bottom: 2px;
    flex: 0 0 auto;
}
.org-avatar img { width: 100%; height: 100%; object-fit: cover; }

.org-name { font-weight: 700; font-size: 0.9rem; line-height: 1.4; }
.org-role { font-size: 0.78rem; color: var(--muted); line-height: 1.5; }

/* The second seat, for somebody who holds two. A chip, so it cannot be read
   as part of the first one. */
.org-extra {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--accent-ink);
    background: var(--accent-soft);
    border-radius: 999px;
    padding: 0.08rem 0.5rem;
    margin-top: 2px;
}

.org-count {
    font-size: 0.72rem;
    color: var(--muted);
    margin-top: 4px;
    padding-top: 4px;
    border-top: 1px solid var(--border-soft);
    width: 100%;
}

/* Depth, so the shape reads before the names do. Only three steps: past the
   third level everybody is a leaf and the distinction stops meaning anything. */
.org-depth-0 {
    border-color: var(--accent);
    box-shadow: 0 3px 14px rgba(76, 70, 214, 0.16);
}
.org-depth-0 .org-avatar { background: var(--accent-soft); color: var(--accent-ink); }
.org-depth-0 .org-name { font-size: 0.98rem; }
.org-depth-1 { border-color: var(--border); }
.org-depth-1 .org-name { font-size: 0.94rem; }
.org-depth-2 .org-name,
.org-depth-3 .org-name { font-weight: 600; }

/* Where the reader is standing. */
.org-card.is-me {
    border-color: var(--accent-ink);
    box-shadow: 0 0 0 2px var(--accent-soft), 0 6px 18px rgba(16, 18, 34, 0.09);
}

/* The connecting lines, softened to match the cards they join. */
.org-node::before,
.org-node::after { border-top-color: var(--border-soft); }
.org-node:last-child::before { border-inline-end-color: var(--border-soft); }
.org-node::after { border-inline-start-color: var(--border-soft); }

@media (prefers-reduced-motion: reduce) {
    .org-card, .org-card:hover { transition: none; transform: none; }
}

@media (max-width: 700px) {
    .org-card { min-width: 156px; max-width: 190px; padding: 0.7rem 0.75rem; }
    .org-avatar { width: 32px; height: 32px; }
}

/* ── مشخصات وظیفه: لیست بورد و ثبت گزارش ─────────────────────────────── */

.board-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin: 0 0 1.25rem;
}
.board-cap { font-size: 0.85rem; color: var(--muted); }
/* The portal gives every form a gap under it; in a row that gap pulls the
   pill 10px above its own label (measured: 142 against 152). */
.board-row .inline-form { margin: 0; }

/* The state is a pill, the way it is on the board itself. The select keeps
   its native behaviour — keyboard, screen reader, the phone's own picker —
   and only its face changes colour with what it holds. */
.board-pick {
    appearance: none;
    -webkit-appearance: none;
    width: auto !important;
    min-width: 8.5rem;
    padding: 0.42rem 2rem 0.42rem 1rem;
    border: 0;
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.84rem;
    font-weight: 700;
    cursor: pointer;
    background-repeat: no-repeat;
    background-position: left 0.7rem center;
    background-size: 0.7rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%231c1c22' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
/* Each ground measured against its own ink, not assumed: all four clear 4.5. */
.board-pick[data-state="todo"]    { background-color: #eceef2; color: #3d3f47; }  /* 9.04:1 */
.board-pick[data-state="doing"]   { background-color: #fdecc0; color: #6a4a00; }  /* 6.92:1 */
.board-pick[data-state="review"]  { background-color: var(--accent-soft); color: var(--accent-ink); } /* 6.79:1 */
.board-pick[data-state="blocked"] { background-color: #fde2de; color: #9b2c1f; }  /* 6.17:1 */
.board-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.board-pick.board-closed {
    display: inline-block; min-width: 0; cursor: default;
    padding-inline-end: 1rem; background-image: none;
    background-color: #dff3e8; color: #17643e;   /* 6.19:1 */
}

.board-weight {
    font-size: 0.8rem;
    color: var(--muted);
    padding: 0.3rem 0.75rem;
    border-radius: 999px;
    background: var(--bg);
}

/* ── the thread ── */
.report-thread { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.25rem; }
.report { display: flex; gap: 0.75rem; align-items: flex-start; }
.report-face {
    flex: 0 0 2.2rem;
    width: 2.2rem; height: 2.2rem;
    border-radius: 50%;
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft);
    color: var(--accent-ink);
    font-weight: 700; font-size: 0.85rem;
}
.report-face img { width: 100%; height: 100%; object-fit: cover; }
.report-body {
    flex: 1; min-width: 0;
    background: var(--bg);
    border-radius: var(--radius);
    padding: 0.7rem 0.95rem;
}
.report-top { display: flex; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.3rem; }
.report-who { font-weight: 700; font-size: 0.86rem; unicode-bidi: plaintext; }
.report-when { font-size: 0.74rem; color: var(--muted); white-space: nowrap; }
.report-text { font-size: 0.9rem; line-height: 1.9; white-space: pre-wrap; overflow-wrap: anywhere; }
.report-called {
    margin-top: 0.5rem;
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem;
    font-size: 0.75rem; color: var(--muted);
}

/* ── the composer, as in the reference: a rounded box, a label, an underline ── */
.report-form {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 0.9rem 1.1rem 0.75rem;
    background: var(--surface);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.report-form:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}
.report-label { display: block; font-size: 0.76rem; color: var(--muted); margin-bottom: 0.25rem; }
.report-form textarea {
    width: 100%;
    border: 0 !important;
    border-bottom: 1px solid var(--border-soft) !important;
    border-radius: 0 !important;
    padding: 0.35rem 0 0.5rem !important;
    background: transparent !important;
    box-shadow: none !important;
    resize: vertical;
    min-height: 3.2rem;
    font-size: 0.92rem;
    line-height: 1.9;
}
.report-form textarea:focus { outline: none; border-bottom-color: var(--accent) !important; }

.report-acts {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.8rem;
}
/* «منشن» and «پیوست» — one look for both, so they read as a pair of tools
   and not as a button beside a link. */
.report-tool {
    all: unset;
    box-sizing: border-box;
    display: inline-flex; align-items: center; gap: 0.35rem;
    min-height: 2.1rem;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.82rem;
    color: #52525c;
    background: var(--bg);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.14s ease, color 0.14s ease;
}
.report-tool:hover,
.report-tool[aria-expanded="true"] { color: var(--accent-ink); background: var(--accent-soft); }
.report-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.report-hint { flex: 1; font-size: 0.76rem; color: var(--muted); padding-inline-start: 0.25rem; }
.report-acts .btn { margin-inline-start: auto; }

/* ── the «@» menu ── */
.mention-menu {
    position: absolute;
    inset-inline-start: 1.1rem;
    top: 5.6rem;
    z-index: 20;
    width: 18rem;
    max-width: calc(100% - 2.2rem);
    max-height: 17rem;
    overflow-y: auto;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    padding: 0.35rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    scrollbar-width: thin;
}
.mention-opt {
    all: unset;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 0.6rem;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-align: start;
}
.mention-opt:hover, .mention-opt.is-active { background: var(--accent-soft); }
.mention-face {
    flex: 0 0 1.9rem;
    width: 1.9rem; height: 1.9rem;
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-soft);
    color: var(--accent-ink);
    font-size: 0.78rem; font-weight: 700;
}
.mention-opt.is-active .mention-face { background: var(--accent); color: #fff; }
.mention-who { display: flex; flex-direction: column; min-width: 0; }
.mention-name {
    font-size: 0.86rem; font-weight: 600; color: var(--text);
    unicode-bidi: plaintext;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mention-unit { font-size: 0.72rem; color: var(--muted); }
.mention-opt.is-picked .mention-name::after { content: " ✓"; color: var(--green-deep); }
.mention-empty { padding: 0.8rem; font-size: 0.82rem; color: var(--muted); text-align: center; }

@media (max-width: 700px) {
    .report-hint { flex-basis: 100%; order: 3; }
    .report-acts .btn { margin-inline-start: 0; }
}

/* A button's label is one line, always. Beside a field that is told to fill
   its row, a button would otherwise give up its width to it and fold «+
   افزودن» into two lines — measured on the checklist: 65px wide, 42px tall.
   The field is the thing that should shrink. */
.btn { white-space: nowrap; }
.inline-form > .btn, .inline-form > button { flex-shrink: 0; }

.mention-tag {
    color: var(--accent-ink);
    background: var(--accent-soft);
    border-radius: 6px;
    padding: 0 0.3rem;
    font-weight: 600;
}

/* ── the rail between menu and page ────────────────────────────────────────
   A fine line in the house colour, fading out at both ends, that bows outward
   halfway down to make a pocket for the button that folds the menu. The bow
   is drawn for a right-to-left page: its straight ends sit on the menu's
   left edge and the bulge points into the page. */
.side-rail {
    position: absolute;
    top: 0;
    bottom: 0;
    inset-inline-end: -29px;
    width: 30px;
    display: flex;
    flex-direction: column;
    pointer-events: none;
}
.side-rail-line {
    flex: 1;
    width: 1.6px;
    align-self: flex-start;          /* the right edge of the box, in RTL */
}
.side-rail-top    { background: linear-gradient(to bottom, rgba(79, 70, 229, 0), rgba(79, 70, 229, 0.55)); }
.side-rail-bottom { background: linear-gradient(to bottom, rgba(79, 70, 229, 0.55), rgba(79, 70, 229, 0)); }
.side-rail-bow { display: block; width: 30px; height: 110px; flex: 0 0 110px; }

.side-fold-btn {
    position: absolute;
    top: calc(50% - 12px);
    inset-inline-end: -26px;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease, transform 0.22s ease;
}
.side-fold-btn:hover { background: var(--accent-soft); }
.side-fold-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.side-fold-btn .icn { stroke-width: 2.2; }

/* Folded: icons only. Wide screens only — below 900px the menu is a drawer
   and has its own button in the top bar. */
@media (min-width: 901px) {
    body.side-folded .sidebar { width: 84px; }
    body.side-folded .side-nav { padding-inline: 0.75rem; }
    body.side-folded .side-link { justify-content: center; padding-inline: 0; gap: 0; }
    body.side-folded .side-label,
    body.side-folded .side-badges { display: none; }
    body.side-folded .side-fold-btn { transform: rotate(180deg); }
    /* Something waiting still shows, as a dot on the icon. */
    body.side-folded .side-link:has(.side-badge) .side-icon { position: relative; }
    body.side-folded .side-link:has(.side-badge) .side-icon::after {
        content: "";
        position: absolute;
        top: 3px;
        inset-inline-end: 3px;
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: var(--red);
        box-shadow: 0 0 0 2px var(--surface);
    }
}
@media (max-width: 900px) {
    .side-rail, .side-fold-btn { display: none; }
}
@media (prefers-reduced-motion: reduce) {
    .sidebar, .side-fold-btn { transition: none; }
}

/* ── the buttons under a task ──
   Never had a rule of their own, so a form and a link simply stacked: «علامت‌
   گذاری به‌عنوان انجام‌شده» sat on «ویرایش وظیفه» with 0px between them.
   One row, the house gap, wrapping on a phone. */
.task-acts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gap-list);
    margin: 0 0 var(--gap-block);
}
.task-acts form { margin: 0; }
.task-acts .btn { margin: 0; }

/* The button under a field inside a card — «ذخیره»، «ذخیره‌ی امضا» — sits a
   list-gap below the field, not 8px under it. */
.panel-body form > .btn,
.profile-avatar-form > .btn,
.sig-actions { margin-top: var(--gap-list); }

/* A folded request button is a block of the page like any other: the panel
   under «درخواست باز کردن برنامه» started 3px below it. */
.record-fold,
.refer-box { margin-bottom: var(--gap-block); }
.refer-box[open] > .refer-form { margin-top: var(--gap-list); }

/* ══ The top bar, white ════════════════════════════════════════════════════
   Colour now lives only in marks — the logo tile, the active icon, the
   primary button, a count, the fine rail beside the menu — and the bar and the
   menu together make one white frame round the page, as in the reference.
   The bar is told from the page by a hairline, and by a soft shadow only once
   something has scrolled under it. */
.topbar {
    background: var(--surface);
    color: var(--text);
    border-bottom: 1px solid var(--border-soft);
    transition: box-shadow 0.18s ease;
}
.topbar.is-scrolled { box-shadow: 0 6px 18px rgba(28, 25, 60, 0.07); }
.topbar-toggle { color: var(--text); }

/* The mark in a purple tile — the same shape as the active icon in the menu,
   so the brand and «you are here» speak one language. The drawing is dark
   line work, painted white here. */
.brand-tile {
    width: 40px; height: 40px;
    border-radius: 12px;
    background: var(--accent);
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    box-shadow: 0 4px 12px rgba(79, 70, 229, 0.28);
}
.topbar .brand-tile .brand-mark { height: 28px; filter: brightness(0) invert(1); }
.brand-name { color: var(--text); }
.workspace-name {
    color: #52525c;                          /* 7.72:1 */
    border-inline-start-color: var(--border);
}

/* The search pill: a pale field on white, with a hairline so it is found. */
.topbar form.topbar-search input[type="search"] {
    background: var(--bg);
    border: 1px solid var(--border-soft);
}
.topbar form.topbar-search input[type="search"]:focus {
    background: var(--surface);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* The controls at the end: pale circles, dark marks, captions kept — «خروج»
   is never something to guess at. */
.topbar-item { color: #52525c; }
.topbar-item:hover { color: var(--text); }
.topbar-disc, .topbar-avatar { opacity: 1; }
.topbar-disc {
    background: var(--bg);
    color: #52525c;                          /* 7.05:1 on the disc */
    transition: background 0.14s ease, color 0.14s ease;
}
.topbar-item:hover .topbar-disc { background: var(--accent-soft); color: var(--accent-ink); }
.topbar-avatar {
    background: var(--accent-soft);
    color: var(--accent-ink);
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}
/* The count keeps its number — it is information — and gets a white edge so
   it reads off the pale disc. */
.bell-count { box-shadow: 0 0 0 2px var(--surface); }
