    /* =====================================================================
       Design scales

       An audit of this stylesheet found 100 hand-written colours, 40 partly
       overlapping variables across three competing palettes, and 38 distinct
       font sizes - .86rem, .87rem, .88rem and .89rem all appear. That is not a
       design, it is accumulation, and repainting on top of it decays the same
       way within weeks.

       These are the scales to reach for from here on. They are additive: the
       existing rules keep working untouched, and each one migrates to a scale
       step as it is next edited, so nothing has to be repainted in one risky
       sweep. New rules should use nothing else.
       ===================================================================== */
    :root {
      /* Type: a 1.125 (major second) ramp. Dense UI needs small steps; a
         bigger ratio forces you to invent sizes between the rungs, which is
         how 38 of them happened. */
      --text-2xs: 0.694rem;
      --text-xs:  0.781rem;
      --text-sm:  0.878rem;
      --text-base: 1rem;
      --text-lg:  1.125rem;
      --text-xl:  1.266rem;
      --text-2xl: 1.424rem;
      --text-3xl: 1.802rem;
      --text-4xl: 2.281rem;

      /* Spacing: 4px base. Every gap, pad and margin is a multiple, so
         rhythm holds without anyone measuring. */
      --space-1: 4px;
      --space-2: 8px;
      --space-3: 12px;
      --space-4: 16px;
      --space-5: 24px;
      --space-6: 32px;
      --space-7: 48px;
      --space-8: 64px;

      /* Radius */
      --radius-sm: 4px;
      --radius-md: 6px;
      --radius-lg: 10px;
      --radius-pill: 999px;

      /* Elevation: three steps only. More than that and nothing reads as
         further forward than anything else. */
      --elev-1: 0 1px 2px rgba(16, 18, 24, .05);
      --elev-2: 0 1px 3px rgba(16, 18, 24, .09);
      --elev-3: 0 6px 18px rgba(16, 18, 24, .13);

      /* Motion: two durations and one curve, so timing is consistent rather
         than picked per component. */
      --dur-fast: .14s;
      --dur-base: .22s;
      --ease-out: cubic-bezier(.2, .8, .3, 1);

      /* Line height */
      --leading-tight: 1.25;
      --leading-normal: 1.5;
    }

    /* Shadows have to be heavier on a dark ground to read at all. */
    :root[data-theme="dark"] {
      --elev-1: 0 1px 2px rgba(0, 0, 0, .4);
      --elev-2: 0 4px 14px rgba(0, 0, 0, .5);
      --elev-3: 0 12px 32px rgba(0, 0, 0, .6);
    }

    /* Two grounds, one product. The marketing site is paper and ink; this is
       the same palette put to a different job - denser, quieter, and built to
       be looked at for eight hours rather than eight seconds. The accent is
       the same violet either side, which is most of why they read as one
       thing. */
    :root, [data-theme="dark"] {
      --bg: #17161a;
      --bg-accent: rgba(139,135,245,.14);
      --bg-accent2: rgba(91,87,216,.08);
      --panel: #1e1d22;
      --panel-solid: #232227;
      --input: #1e1d22;
      --line: rgba(255,255,255,.11);
      --text: #f3f2ee;
      --muted: #a9a69c;

      /* Four tones for everything that classifies: priority, type, status.
         The same four the site uses, so a peach chip means the same thing on
         the landing page and on a card. */
      --tone-hot-bg: rgba(248,217,189,.16);   --tone-hot-ink: #f6bd90;
      --tone-warm-bg: rgba(245,205,71,.14);   --tone-warm-ink: #e8c65c;
      --tone-good-bg: rgba(207,232,214,.14);  --tone-good-ink: #93d3ab;
      --tone-cool-bg: rgba(212,226,250,.14);  --tone-cool-ink: #a8c4ee;
      --tone-idea-bg: rgba(224,217,255,.16);  --tone-idea-ink: #bdb2f7;
      --teal: #8b87f5;
      --teal-dim: #5b57d8;
      --btn-text: #ffffff;
      --shadow: 0 1px 3px rgba(0,0,0,.55);
      --danger: #f87168;
      --top-bg: #1d2125;
      --side-bg: #1d2125;
      --col-bg: #161a1d;
      --card-bg: #22272b;
      --accent-blue: #5b57d8;
    }
    [data-theme="light"] {
      /* Atlassian's neutrals, because that is what was asked for: this is a
         board people sit in front of all day beside Jira itself, and matching
         the greys they already read costs nothing. The warm paper stays on
         the marketing site, where the job is to look unlike everything else.
         The accent is still ours. */
      --bg: #f7f8f9;
      --bg-accent: rgba(91,87,216,.09);
      --bg-accent2: rgba(91,87,216,.06);
      --panel: #ffffff;
      --panel-solid: #ffffff;
      --input: #ffffff;
      --line: #dfe1e6;
      --text: #172b4d;
      --muted: #626f86;
      --teal: #5b57d8;
      --teal-dim: #4a46c4;
      --btn-text: #ffffff;
      --shadow: 0 1px 1px rgba(9,30,66,.25), 0 0 1px rgba(9,30,66,.31);
      --danger: #c9372c;
      --top-bg: #ffffff;
      --side-bg: #ffffff;
      --col-bg: #f1f2f4;
      --card-bg: #ffffff;
      --accent-blue: #5b57d8;

      --tone-hot-bg: #ffedeb;   --tone-hot-ink: #ae2e24;
      --tone-warm-bg: #fff7d6;  --tone-warm-ink: #7f5f01;
      --tone-good-bg: #dcfff1;  --tone-good-ink: #216e4e;
      --tone-cool-bg: #e9f2ff;  --tone-cool-ink: #0055cc;
      --tone-idea-bg: #f3f0ff;  --tone-idea-ink: #5e4db2;
    }
    [data-theme="ocean"] {
      --bg: #061526;
      --bg-accent: rgba(6,182,212,.22);
      --bg-accent2: rgba(37,99,235,.16);
      --panel: rgba(10,28,48,.90);
      --panel-solid: #0c2238;
      --input: #071828;
      --line: rgba(125,211,252,.18);
      --text: #e0f2fe;
      --muted: #7dd3fc;
      --teal: #22d3ee;
      --teal-dim: #0284c7;
      --btn-text: #042f2e;
      --shadow: 0 18px 50px rgba(0,0,0,.4);
      --danger: #fda4af;
      --top-bg: rgba(6,21,38,.82);
      --side-bg: #0c2238;
      --col-bg: #061526;
      --card-bg: #0e2740;
      --accent-blue: #0284c7;
    }
    [data-theme="forest"] {
      --bg: #0a1410;
      --bg-accent: rgba(34,197,94,.18);
      --bg-accent2: rgba(16,185,129,.10);
      --panel: rgba(18,36,28,.90);
      --panel-solid: #14261c;
      --input: #0c1a12;
      --line: rgba(134,239,172,.16);
      --text: #ecfdf5;
      --muted: #86efac;
      --teal: #22c55e;
      --teal-dim: #15803d;
      --btn-text: #052e16;
      --shadow: 0 18px 50px rgba(0,0,0,.38);
      --danger: #fda4af;
      --top-bg: rgba(10,20,16,.82);
      --side-bg: #14261c;
      --col-bg: #0a1410;
      --card-bg: #172d21;
      --accent-blue: #15803d;
    }
    [data-theme="sunset"] {
      --bg: #1a1410;
      --bg-accent: rgba(245,158,11,.16);
      --bg-accent2: rgba(251,113,133,.10);
      --panel: rgba(36,28,24,.90);
      --panel-solid: #2a201a;
      --input: #1c1612;
      --line: rgba(253,186,116,.18);
      --text: #fff7ed;
      --muted: #fdba74;
      --teal: #f59e0b;
      --teal-dim: #b45309;
      --btn-text: #1c1008;
      --shadow: 0 18px 50px rgba(0,0,0,.4);
      --danger: #fda4af;
      --top-bg: rgba(26,20,16,.84);
      --side-bg: #2a201a;
      --col-bg: #1a1410;
      --card-bg: #322620;
      --accent-blue: #b45309;
    }
    * { box-sizing: border-box; }
    /* Base for every rem in the sheet. The browser default of 16px made the
       app read oversized. 14px is what dense work tools run at, and trims
       about 12% off all type at once without touching a single component. */
    html { font-size: 14px; }

    /* iOS Safari's 100vh is the viewport with the toolbars *hidden*, which
       they are not while you are reading. So a 100vh shell is about a hundred
       pixels taller than the screen, and the bottom of it - the board's last
       cards, a dialog's Save button - sits underneath the browser chrome
       where nothing can reach it. `dvh` is the height that is actually
       visible, and it follows the toolbars as they slide away.
       The vh line stays first as the fallback for anything without dvh. */
    :root { --app-h: 100vh; }
    @supports (height: 100dvh) { :root { --app-h: 100dvh; } }
    html, body { height: auto; min-height: 100%; margin: 0; overflow-x: hidden; overflow-y: auto; max-width: 100vw; }
    body {
      font-family: "Inter Tight", "Noto Sans Arabic", system-ui, sans-serif;
      color: var(--text);
      background:
        radial-gradient(1100px 520px at 8% -8%, var(--bg-accent), transparent 55%),
        radial-gradient(900px 480px at 100% 0%, var(--bg-accent2), transparent 50%),
        var(--bg);
      min-height: var(--app-h);
      /* `color` is deliberately NOT transitioned. It resolves from var(--text),
         and when the theme swaps the data-theme attribute the colour transition
         strands halfway — leaving body (and everything inheriting from it) stuck
         on the previous theme's text colour, e.g. near-white text on the light
         theme's white sidebar. Background is safe to fade. */
      transition: background .35s ease;
    }
    /* Belt and braces: kill every transition for one frame while the theme
       swaps, so no other animated property can strand the same way. */
    html[data-theme-switching] *,
    html[data-theme-switching] *::before,
    html[data-theme-switching] *::after {
      transition: none !important;
    }
    body.surface-light {
      background: #fff !important;
      color: #1a1a2e;
    }
    html[dir="rtl"] body { font-family: "Noto Sans Arabic", "Inter Tight", system-ui, sans-serif; }
    button, input, select, textarea { font: inherit; color: inherit; }
    .hidden { display: none !important; }
    html[data-boot="app"] #appView.shell {
      display: grid !important;
    }
    html[data-boot="ws"] #wsView.auth-wrap,
    html[data-boot="auth"] #authView.auth-wrap {
      display: flex !important;
    }
    html[data-boot="app"] body,
    html[data-boot="ws"] body,
    html[data-boot="auth"] body {
      background: var(--bg, #161a1d);
    }
    html[data-boot="ws"] body,
    html[data-boot="auth"] body {
      background: #fff;
    }
    .brand { font-family: "Inter Tight", "Noto Sans Arabic", system-ui, sans-serif; font-size: 1.02rem; font-weight: 600; letter-spacing: -.015em; }
    .brand span { color: var(--teal); }
    .fade-in { animation: fadeUp .45s ease both; }
    @keyframes fadeUp {
      from { opacity: 0; transform: translateY(10px); }
      to { opacity: 1; transform: none; }
    }
    @keyframes pop {
      from { opacity: 0; transform: scale(.96); }
      to { opacity: 1; transform: none; }
    }
    @keyframes pulse {
      0%, 100% { transform: scale(1); }
      50% { transform: scale(1.08); }
    }
    .auth-wrap {
      min-height: var(--app-h);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      padding: 56px 16px 40px;
      position: relative;
      overflow: visible;
      background: #fff;
    }
    html[data-theme="ocean"] .auth-wrap,
    html[data-theme="slate"] .auth-wrap,
    body.theme-dark .auth-wrap {
      background: #fff;
    }
    .auth-wrap::before,
    .auth-wrap::after { display: none; }
    .auth-card, .panel, .modal {
      background: var(--panel);
      border: 1px solid var(--line);
      box-shadow: var(--shadow);
      backdrop-filter: blur(12px);
    }
    .auth-card {
      width: min(400px, 100%);
      border-radius: 12px;
      padding: 28px 28px 22px;
      animation: pop .35s ease;
      position: relative; z-index: 1;
      background: #fff;
      border: 1px solid rgba(22, 21, 15, .12);
      box-shadow: 0 1px 2px rgba(22, 21, 15, .05),
                  0 18px 44px -24px rgba(22, 21, 15, .3);
      color: #172b4d;
    }
    html[data-theme="ocean"] .auth-card,
    html[data-theme="slate"] .auth-card {
      background: var(--panel-solid, var(--panel));
      border-color: var(--line);
      color: var(--text);
      box-shadow: var(--shadow);
    }
    .auth-card .atl-logo {
      display: flex; justify-content: center; align-items: center; gap: 10px;
      margin-bottom: 18px; font-weight: 600; font-size: 1.1rem; letter-spacing: -.02em;
    }
    .auth-card .atl-logo img.mark,
    .brand-mark {
      width: 36px; height: 36px; border-radius: 9px;
      display: block; flex-shrink: 0;
      box-shadow: 0 4px 12px rgba(79, 70, 229, .28);
    }
    .auth-card .atl-logo .word {
      color: #312E81;
      font-weight: 600;
    }
    html[data-theme="ocean"] .auth-card .atl-logo .word,
    html[data-theme="slate"] .auth-card .atl-logo .word,
    html[data-theme="dark"] .auth-card .atl-logo .word {
      color: var(--text);
    }
    .app-side-brand {
      display: flex; align-items: center; gap: 8px;
    }
    .app-side-brand .brand-mark { width: 28px; height: 28px; border-radius: 7px; }
    .ws-brand-row {
      display: flex; align-items: center; gap: 10px;
    }
    .ws-brand-row .brand-mark { width: 32px; height: 32px; border-radius: 8px; }
    .auth-card h1.atl-title {
      margin: 0 0 18px; text-align: center; font-size: 1.15rem; font-weight: 600;
      color: inherit;
    }
    .auth-card p { color: var(--muted); margin: 8px 0 20px; line-height: 1.5; }
    .auth-card label { display: block; font-size: .85rem; color: #626f86; margin: 0 0 6px; font-weight: 600; }
    html[data-theme="ocean"] .auth-card label,
    html[data-theme="slate"] .auth-card label { color: var(--muted); }
    .auth-card input[type="email"],
    .auth-card input[type="password"],
    .auth-card input[type="text"],
    .auth-card input:not([type]) {
      width: 100%; background: #f7f8f9; border: 1px solid #dfe1e6;
      border-radius: 8px; padding: 10px 12px; outline: none; color: #172b4d;
    }
    html[data-theme="ocean"] .auth-card input,
    html[data-theme="slate"] .auth-card input {
      background: var(--input); border-color: var(--line); color: var(--text);
    }
    .auth-card input:focus { border-color: #5350cf; box-shadow: 0 0 0 1px #5350cf; }
    .auth-remember {
      display: flex; align-items: center; gap: 8px; margin: 10px 0 2px;
      font-size: var(--text-xs); color: #6f6c5e; cursor: pointer; user-select: none;
    }
    .auth-remember input { width: auto; accent-color: #5350cf; }
    .btn.atl-primary {
      width: 100%; margin-top: 14px; border: 0; border-radius: var(--radius-pill);
      padding: 11px 16px; background: #5b46e5; color: #fff; font-weight: 500;
      cursor: pointer; font-size: .95rem;
    }
    .btn.atl-primary:hover { background: #4b39cc; filter: none; }
    .btn.atl-primary:disabled { opacity: .65; cursor: wait; }
    .oauth-divider {
      display: flex; align-items: center; gap: 12px; margin: 16px 0 12px;
      color: #6f6c5e; font-size: var(--text-2xs); text-align: center;
    }
    .oauth-divider::before, .oauth-divider::after {
      content: ""; flex: 1; height: 1px; background: #dfe1e6;
    }
    html[data-theme="ocean"] .oauth-divider::before,
    html[data-theme="ocean"] .oauth-divider::after,
    html[data-theme="slate"] .oauth-divider::before,
    html[data-theme="slate"] .oauth-divider::after { background: var(--line); }
    .sso-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
    .sso-btn {
      display: flex; align-items: center; justify-content: center; gap: 7px;
      width: 100%; margin: 0; padding: 10px 8px; border-radius: 9px;
      border: 1px solid var(--line, #dfe1e6); background: #fff; color: #16150f;
      font-weight: 500; cursor: pointer; font-size: var(--text-xs);
      min-height: 42px;
    }
    .sso-btn:hover { background: #faf9f3; }
    /* The logos are the label; the words only fit once there is room for
       them, and nobody has ever needed to be told which one is Google. */
    .sso-name { display: none; }
    @media (min-width: 420px) { .sso-name { display: inline; } }
    html[data-theme="ocean"] .sso-btn,
    html[data-theme="slate"] .sso-btn {
      background: var(--input); border-color: var(--line); color: var(--text);
    }
    .sso-btn .sso-ico { width: 18px; height: 18px; flex-shrink: 0; }
    .sso-btn:disabled { opacity: .5; cursor: not-allowed; }
    .auth-footer-links {
      display: flex; justify-content: center; gap: 10px; align-items: center;
      margin-top: 18px; flex-wrap: wrap; font-size: .9rem;
    }
    .auth-footer-links .dot { color: #8993a4; }
    .auth-footer-links button.linkish,
    .auth-footer-links a.linkish {
      background: none; border: 0; color: #5350cf; cursor: pointer;
      padding: 0; font-weight: 600; width: auto; margin: 0;
    }
    .auth-subfoot {
      margin-top: 18px; text-align: center; font-size: .75rem; color: #626f86;
    }
    .auth-toolbar {
      position: absolute; top: 14px; inset-inline-end: 14px;
      display: flex; gap: 8px; align-items: center; z-index: 2;
    }
    .auth-step-back {
      background: none; border: 0; color: #5350cf; cursor: pointer;
      font-weight: 600; padding: 0; margin: 0 0 10px; font-size: .85rem;
    }
    .auth-email-chip {
      display: inline-flex; align-items: center; gap: 6px;
      background: #f1f2f4; border-radius: 999px; padding: 4px 10px;
      font-size: .8rem; margin-bottom: 12px; color: #44546f;
    }
    html[data-theme="ocean"] .auth-email-chip,
    html[data-theme="slate"] .auth-email-chip { background: var(--input); color: var(--muted); }
    label { display: block; font-size: .85rem; color: var(--muted); margin: 12px 0 6px; }
    /* A field in a dense tool is something you fill in, not a feature. The
       old 12px padding on a 12px radius made every form look like a landing
       page - which is exactly where that shape belongs and this is not it. */
    input, select, textarea {
      width: 100%; background: var(--input); border: 1px solid var(--line);
      border-radius: 9px; padding: 9px 12px; outline: none;
      transition: border-color var(--dur-fast) var(--ease-out),
                  box-shadow var(--dur-fast) var(--ease-out);
    }
    input:focus, select:focus, textarea:focus {
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-blue) 16%, transparent);
    }
    input:focus, select:focus, textarea:focus { border-color: var(--teal); }
    textarea { min-height: 90px; resize: vertical; }
    .btn {
      border: 0; border-radius: var(--radius-pill); padding: 9px 16px; cursor: pointer;
      background: var(--accent-blue, var(--teal-dim));
      color: #fff; font-weight: 500; width: 100%; margin-top: 16px;
      font-size: var(--text-sm);
      transition: background var(--dur-fast) var(--ease-out),
                  filter var(--dur-fast) var(--ease-out);
    }
    .btn:hover { filter: brightness(1.08); transform: none; }
    .btn.secondary { background: transparent; border: 1px solid var(--line); color: var(--text); }
    .btn.ghost { width: auto; background: transparent; border: 1px solid transparent; color: var(--text); padding: 6px 12px; margin: 0; border-radius: var(--radius-pill); }
    .btn.ghost:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
    .btn.create {
      width: auto; margin: 0; background: var(--accent-blue, #5b57d8); color: #fff;
      border-radius: var(--radius-pill); padding: 7px 16px; font-weight: 500;
    }
    .btn.danger {
      background: transparent; border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
      color: var(--danger); border-radius: var(--radius-pill);
    }
    .error { color: var(--danger); font-size: .9rem; margin-top: 10px; min-height: 1.2em; }
    .shell {
      display: grid;
      grid-template-columns: 220px minmax(0, 1fr);
      grid-template-rows: 1fr;
      min-height: var(--app-h);
      max-width: 100%;
      overflow: hidden;
      background: var(--bg);
      transition: grid-template-columns .2s ease;
    }
    .shell.side-collapsed {
      grid-template-columns: 0 minmax(0, 1fr);
    }
    .shell.side-collapsed .app-side {
      overflow: hidden;
      padding: 0;
      border: 0;
      opacity: 0;
      pointer-events: none;
    }
    .app-side {
      grid-column: 1;
      grid-row: 1;
      background: var(--side-bg, var(--panel-solid));
      border-inline-end: 1px solid var(--line);
      display: flex;
      flex-direction: column;
      gap: 4px;
      padding: 12px 10px;
      min-width: 0;
      overflow-y: auto;
      z-index: 9;
      transition: opacity .15s ease, padding .15s ease;
    }
    .app-main {
      grid-column: 2;
      grid-row: 1;
      min-width: 0;
      display: grid;
      grid-template-rows: auto auto 1fr;
      max-height: var(--app-h);
      overflow: hidden;
    }
    html[dir="rtl"] .shell {
      grid-template-columns: minmax(0, 1fr) 220px;
    }
    html[dir="rtl"] .shell.side-collapsed {
      grid-template-columns: minmax(0, 1fr) 0;
    }
    html[dir="rtl"] .app-side { grid-column: 2; border-inline-end: 0; border-inline-start: 1px solid var(--line); }
    html[dir="rtl"] .app-main { grid-column: 1; }

    .app-side-brand { padding: 6px 10px 14px; font-weight: 600; font-size: 1rem; }
    .app-side-top {
      display: flex; align-items: center; justify-content: space-between; gap: 6px;
    }
    .app-side-top .app-side-brand { padding-bottom: 6px; flex: 1; min-width: 0; }
    .app-side-label {
      font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
      color: var(--muted); padding: 10px 10px 4px; font-weight: 600;
    }
    .app-side-space {
      margin: 4px 6px 10px; padding: 8px 10px; border-radius: 6px;
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 28%, transparent);
      border: 1px solid color-mix(in srgb, var(--accent-blue, #5b57d8) 45%, transparent);
      font-size: .85rem; font-weight: 600;
    }
    .app-side-space .muted { font-size: .72rem; font-weight: 500; display: block; margin-top: 2px; }
    .top {
      display: flex; align-items: center; gap: 10px; justify-content: space-between; flex-wrap: wrap;
      padding: 8px 14px; border-bottom: 1px solid var(--line);
      background: var(--top-bg); position: sticky; top: 0; z-index: 8;
      max-width: 100%; box-sizing: border-box; min-height: 52px;
    }
    .top-search {
      flex: 1 1 220px; max-width: 420px; min-width: 140px;
      display: flex; align-items: center; gap: 8px;
      background: var(--input); border: 1px solid var(--line);
      border-radius: var(--radius-pill);
      padding: 7px 14px; color: var(--muted); font-size: var(--text-sm);
    }
    .top-search { position: relative; }
    .top-search input {
      border: 0; background: transparent; padding: 0; width: 100%; outline: none;
      color: var(--text); font: inherit;
    }
    /* type=search draws its own clear button, next to ours. One cross is a
       control; two side by side is a bug people click on twice. */
    .top-search input::-webkit-search-cancel-button,
    .top-search input::-webkit-search-decoration { -webkit-appearance: none; }
    .top-search-ico { width: 16px; height: 16px; flex: 0 0 auto; opacity: .8; }
    .top-search-clear {
      border: 0; background: transparent; color: var(--muted); cursor: pointer;
      font-size: var(--text-2xs); line-height: 1; padding: 4px; margin: 0;
      border-radius: 999px; flex: 0 0 auto;
    }
    .top-search-clear:hover { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text); }

    /* ===== Search results =====
       Grouped, because the first question a reader has is not "which row" but
       "is the thing I want a task at all". The panel hangs off the search so
       it follows the pill when that collapses to an icon on a phone. */
    /* ===== Full-size picture =====
       A screenshot pasted into a task is unreadable at the width of a
       description column. Clicking it opens it over everything; clicking
       anywhere that is not the picture puts it away. */
    .lightbox {
      position: fixed; inset: 0; z-index: 200;
      display: grid; place-items: center; padding: 4vh 4vw;
      background: rgba(9, 10, 12, .82);
      animation: fade var(--dur-fast) var(--ease-out);
      cursor: zoom-out;
    }
    .lightbox-img {
      max-width: 92vw; max-height: 88vh; width: auto; height: auto;
      border-radius: var(--radius-md, 6px);
      box-shadow: 0 24px 70px rgba(0,0,0,.5);
      cursor: default; background: #fff;
    }
    .lightbox-x {
      position: absolute; top: 14px; inset-inline-end: 16px;
      width: 36px; height: 36px; padding: 0; margin: 0; border: 0;
      border-radius: 999px; cursor: pointer;
      background: rgba(255,255,255,.14); color: #fff; font-size: 15px;
      display: grid; place-items: center;
    }
    .lightbox-x:hover { background: rgba(255,255,255,.26); }
    @keyframes fade { from { opacity: 0 } to { opacity: 1 } }
    /* In the editor a picture advertises that it can be opened. */
    .desc-editor img { cursor: zoom-in; max-width: 100%; height: auto; }

    .search-panel {
      position: absolute; top: calc(100% + 8px); inset-inline-start: 0;
      width: min(520px, calc(100vw - 24px)); max-height: min(60vh, 480px);
      overflow-y: auto; z-index: 45; padding: 6px;
      background: var(--panel-solid); border: 1px solid var(--line);
      border-radius: var(--radius-md, 10px);
      box-shadow: 0 16px 40px rgba(0,0,0,.28);
      animation: pop var(--dur-fast) var(--ease-out);
      text-align: start;
    }
    .search-panel.hidden { display: none !important; }
    .search-group + .search-group { margin-top: 4px; }
    .search-group-head {
      padding: 8px 10px 4px; font-size: var(--text-2xs); font-weight: 700;
      letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
    }
    .search-row {
      display: flex; align-items: flex-start; gap: 10px; width: 100%;
      margin: 0; padding: 8px 10px; border: 0; border-radius: 8px;
      background: transparent; color: var(--text); cursor: pointer;
      text-align: start; font: inherit;
    }
    .search-row:hover, .search-row.is-active { background: color-mix(in srgb, var(--text) 8%, transparent); }
    .search-key {
      flex: 0 0 auto; font-size: var(--text-2xs); font-weight: 700;
      color: var(--muted); background: var(--col-bg); border-radius: 4px;
      padding: 3px 6px; margin-top: 1px; white-space: nowrap;
      font-variant-numeric: tabular-nums; direction: ltr;
    }
    .search-dot {
      flex: 0 0 auto; width: 10px; height: 10px; border-radius: 3px;
      margin: 6px 4px 0;
    }
    .search-face {
      flex: 0 0 auto; width: 24px; height: 24px; border-radius: 999px;
      display: grid; place-items: center; color: #fff;
      font-size: var(--text-2xs); font-weight: 700;
    }
    .search-body { min-width: 0; display: grid; gap: 2px; }
    .search-title {
      font-size: var(--text-sm); line-height: 1.35;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .search-sub {
      font-size: var(--text-2xs); color: var(--muted); line-height: 1.35;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .search-row mark {
      background: color-mix(in srgb, var(--accent-blue, #0c66e4) 22%, transparent);
      color: inherit; border-radius: 3px; padding: 0 1px;
    }
    .search-note {
      display: grid; gap: 4px; padding: 18px 14px; text-align: center;
      font-size: var(--text-sm); color: var(--muted);
    }
    .search-note strong { color: var(--text); }
    .search-foot {
      padding: 8px 10px 4px; font-size: var(--text-2xs); color: var(--muted);
      border-top: 1px solid var(--line); margin-top: 6px; text-align: center;
    }
    .top-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

    /* ===== Account menu =====
       One avatar in place of four header controls. */
    .account-wrap { position: relative; display: inline-flex; }
    .account-btn {
      width: 30px; height: 30px; padding: 0; margin: 0; border: 0; border-radius: 999px;
      background: transparent; cursor: pointer; display: grid; place-items: center;
      box-shadow: 0 0 0 2px transparent; transition: box-shadow .15s;
    }
    .account-btn:hover, .account-btn[aria-expanded="true"] {
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-blue, #5b57d8) 45%, transparent);
    }
    .account-btn .avatar-chip { width: 28px; height: 28px; font-size: .68rem; }
    .account-menu {
      position: absolute; inset-inline-end: 0; top: calc(100% + 8px); z-index: 40;
      width: 268px; padding: 6px; border-radius: 10px;
      background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: 0 16px 40px rgba(0,0,0,.28);
      animation: pop .14s ease;
    }
    .account-menu.hidden { display: none !important; }
    .account-id { display: flex; align-items: center; gap: 10px; padding: 10px 10px 12px; }
    .account-id-avatar { width: 34px; height: 34px; font-size: .8rem; }
    .account-id-text { min-width: 0; display: grid; }
    .account-id-text strong { font-size: .92rem; }
    .account-id-text .muted {
      font-size: .78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .account-sep { height: 1px; margin: 4px 0; background: var(--line); }
    .account-group-label {
      padding: 8px 10px 6px; font-size: .7rem; font-weight: 600;
      letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
    }
    .account-item {
      display: flex; align-items: center; gap: 10px; width: 100%; margin: 0;
      padding: 9px 10px; border: 0; border-radius: 7px; background: transparent;
      color: var(--text); font: inherit; text-align: start; cursor: pointer;
    }
    .account-item:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
    .account-item span[aria-hidden] { width: 16px; text-align: center; opacity: .7; }
    .account-item.danger { color: #e5484d; }
    .account-item.danger:hover { background: color-mix(in srgb, #e5484d 12%, transparent); }
    .account-lang { margin: 0 10px 6px; }

    /* Themes are picked from what they look like, not from their names. */
    .theme-choices { display: grid; gap: 2px; padding: 0 4px 4px; }
    .theme-choice {
      display: flex; align-items: center; gap: 10px; width: 100%; margin: 0;
      padding: 6px 6px; border: 1px solid transparent; border-radius: 7px;
      background: transparent; color: var(--text); font: inherit;
      text-align: start; cursor: pointer; font-size: .85rem;
    }
    .theme-choice:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
    .theme-choice.on {
      border-color: color-mix(in srgb, var(--accent-blue, #5b57d8) 55%, transparent);
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 10%, transparent);
    }
    /* A miniature of the page: its ground, and a stripe of its accent. */
    .theme-swatch {
      width: 34px; height: 22px; border-radius: 4px; flex: 0 0 auto;
      border: 1px solid color-mix(in srgb, var(--text) 22%, transparent);
      display: block; position: relative; overflow: hidden;
    }
    .theme-swatch i {
      position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 9px;
      display: block;
    }
    .theme-choice.on .theme-swatch {
      border-color: color-mix(in srgb, var(--accent-blue, #5b57d8) 70%, transparent);
    }
    .project-tabs {
      display: flex; gap: 2px; flex-wrap: wrap; align-items: center;
      padding: 0 14px; border-bottom: 1px solid var(--line);
      background: var(--panel);
      overflow-x: auto;
    }
    .project-tabs button {
      background: transparent; border: 0; border-bottom: 2px solid transparent;
      color: var(--muted); padding: 12px 12px 10px; cursor: pointer;
      font-weight: 600; font-size: .88rem; border-radius: 0; white-space: nowrap;
    }
    .project-tabs button.active, .project-tabs button:hover {
      color: var(--text); border-bottom-color: var(--accent-blue, var(--teal));
    }
    .top-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
    .top .brand { font-size: 1.05rem; }
    .theme-select {
      width: auto; min-width: 0; max-width: 140px; padding: 6px 10px; border-radius: 3px;
      background: var(--input); border: 1px solid var(--line); cursor: pointer;
    }
    .row select, .top select { width: auto; max-width: min(260px, 100%); flex: 0 1 auto; }
    .nav { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
    /* Navigation is furniture. It was set in 600 like everything else, which
       is how a page ends up with no hierarchy: when it is all bold, none of
       it is. */
    .nav button {
      background: transparent; border: 1px solid transparent; color: var(--muted);
      padding: 7px 11px; border-radius: var(--radius-lg); cursor: pointer;
      transition: background var(--dur-fast) var(--ease-out),
                  color var(--dur-fast) var(--ease-out);
      text-align: start; font-weight: 450; font-size: var(--text-sm); width: 100%;
    }
    .nav button:hover {
      color: var(--text); background: color-mix(in srgb, var(--text) 6%, transparent);
    }
    .nav button.active {
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 13%, transparent);
      color: var(--text); font-weight: 500;
    }
    .lang-toggle {
      display: inline-flex; border: 1px solid var(--line); border-radius: 3px; overflow: hidden;
    }
    .lang-toggle button {
      background: transparent; border: 0; color: var(--muted); padding: 6px 10px; cursor: pointer;
      font-weight: 600; font-size: .8rem;
    }
    .lang-toggle button.active {
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 28%, transparent); color: var(--text);
    }
    .content {
      padding: 12px 14px 20px; max-width: 100%; overflow-x: hidden; overflow-y: auto;
      box-sizing: border-box; min-height: 0;
    }
    .panel {
      border-radius: 8px; padding: 12px; animation: fadeUp .35s ease;
      max-width: 100%; overflow-x: hidden; box-sizing: border-box;
      background: transparent; border: 0; box-shadow: none; backdrop-filter: none;
    }
    /* Calendar panels size to the viewport so a whole month fits without the
       page scrolling; the grid rows share whatever height is left. */
    .panel.panel-fill {
      height: 100%;
      display: flex;
      flex-direction: column;
      min-height: 0;
      padding-bottom: 4px;
    }
    .panel.panel-fill.hidden { display: none; }
    .row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; justify-content: space-between; margin-bottom: 12px; }
    /* Was an inline style on each panel toolbar, which no stylesheet rule could
       override - including the small-screen layout. */
    .row-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
    .muted { color: var(--muted); }
    /* Jira-style board: the board scrolls sideways, each column scrolls its own
       cards vertically, and the column header + "Create" stay pinned. */
    .kanban {
      display: flex; gap: 12px; overflow-x: auto; width: 100%;
      padding: 8px 4px 16px;
      height: calc(var(--app-h) - 196px);
      min-height: 420px;
      align-items: stretch;
      background: color-mix(in srgb, var(--text) 4%, var(--bg));
      border-radius: var(--radius-lg);
    }
    .col {
      background: var(--col-bg, #161a1d);
      border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
      border-radius: 10px;
      padding: 8px;
      transition: background .2s, box-shadow .2s;
      flex: 1 0 272px; min-width: 272px; max-width: 320px;
      display: flex; flex-direction: column; gap: 6px;
      min-height: 0;              /* let the cards area shrink and scroll */
      max-height: 100%;
    }
    .col.drag-over { box-shadow: inset 0 0 0 2px var(--accent-blue, var(--teal)); }
    .col.col-dragging { opacity: .55; }
    .col-cards {
      display: flex;
      flex-direction: column;
      gap: 0;
      flex: 1 1 auto;      /* takes the leftover height so the header stays put */
      min-height: 0;
      overflow-y: auto;
      overflow-x: hidden;
      padding-inline-end: 2px;   /* breathing room beside the scrollbar */
      scrollbar-width: thin;
      scrollbar-color: color-mix(in srgb, var(--text) 22%, transparent) transparent;
    }
    .col-cards::-webkit-scrollbar { width: 8px; }
    .col-cards::-webkit-scrollbar-track { background: transparent; }
    .col-cards::-webkit-scrollbar-thumb {
      background: color-mix(in srgb, var(--text) 18%, transparent);
      border-radius: 999px;
    }
    .col-cards:hover::-webkit-scrollbar-thumb {
      background: color-mix(in srgb, var(--text) 30%, transparent);
    }
    .col-head, .col-create { flex: 0 0 auto; }
    .col-create {
      width: 100%; margin: 2px 0 0; padding: 8px 10px; border: 0; border-radius: 6px;
      background: transparent; color: var(--muted); font-weight: 600; cursor: pointer;
      text-align: start; font-size: .88rem; flex: 0 0 auto;
    }
    .col-create:hover {
      background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text);
    }
    /* The board's single toolbar: what you are looking at on the left, what you
       can do to it on the right. */
    .board-bar {
      display: flex; align-items: center; justify-content: space-between;
      gap: 12px; flex-wrap: wrap; margin: 0 0 10px; min-width: 0;
    }
    .board-bar-project {
      display: flex; align-items: baseline; gap: 10px; min-width: 0; flex: 0 1 auto;
    }
    /* The picker is no longer inside .row, so it needs its own sizing - without
       this it inherits width:100% from the global select rule and then gets
       flex-shrunk to a sliver on a narrow screen. */
    .board-bar-project select {
      flex: 0 1 auto; width: auto; min-width: 180px; max-width: min(280px, 100%);
      /* Project keys are Latin and lead the label ("WTM - Platform"). In an RTL
         page the bidi reorder put the key last, so a narrow picker clipped the
         one part that identifies the project. Pin the control to LTR; an Arabic
         project name inside it still renders right-to-left on its own. */
      direction: ltr; text-align: start;
    }
    .board-bar-tools {
      display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
      position: relative;   /* anchors the people-filter menu */
    }
    .board-bar-sep {
      width: 1px; height: 20px; flex: 0 0 auto;
      background: color-mix(in srgb, var(--text) 14%, transparent);
    }
    .board-bar-tools .btn { margin: 0; }
    .board-filter-search {
      flex: 1 1 180px; min-width: 140px; max-width: 280px; margin: 0;
      padding: 8px 10px; border-radius: 4px;
    }
    .assignee-filter {
      /* One row, never wrapping: overflow goes behind the "+N" chip instead. */
      display: flex; align-items: center; gap: 4px; flex-wrap: nowrap;
    }
    .assignee-filter-btn {
      width: 26px; height: 26px; border-radius: var(--radius-pill);
      border: 2px solid transparent;
      background: #6554c0; color: #fff; font-size: var(--text-2xs); font-weight: 600;
      cursor: pointer; padding: 0; display: grid; place-items: center;
      filter: saturate(.5) opacity(.68);
      transition: filter var(--dur-fast) var(--ease-out);
    }
    .assignee-filter-btn.active {
      border-color: var(--accent-blue, #5b57d8);
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-blue, #5b57d8) 35%, transparent);
    }
    .assignee-filter-btn.all {
      background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text);
      font-size: var(--text-2xs); width: auto; padding: 0 11px;
      border-radius: var(--radius-pill); filter: none;
    }
    .assignee-filter-btn.unassigned {
      background: color-mix(in srgb, var(--muted) 35%, transparent); color: var(--text);
    }
    .assignee-filter-btn.more {
      background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--muted);
      font-size: .7rem; font-weight: 600;
    }
    .assignee-filter-btn.more:hover {
      background: color-mix(in srgb, var(--text) 16%, transparent); color: var(--text);
    }
    .assignee-filter-btn:hover,
    .assignee-filter-btn.active { filter: none; }
    .people-filter-wrap { position: relative; display: inline-flex; align-items: center; gap: 8px; }
    .people-filter-btn {
      width: auto; margin: 0; display: inline-flex; align-items: center; gap: 8px;
      padding: 6px 13px; border-radius: var(--radius-pill); border: 1px solid var(--line);
      background: transparent; color: var(--text);
      font-weight: 500; font-size: var(--text-sm); cursor: pointer;
    }
    .people-filter-btn:hover { background: color-mix(in srgb, var(--text) 10%, transparent); }
    .people-filter-btn.active {
      border-color: color-mix(in srgb, var(--accent-blue, #5b57d8) 55%, transparent);
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 16%, transparent);
    }
    .people-filter-btn .count-pill {
      min-width: 18px; height: 18px; border-radius: 999px; padding: 0 6px;
      background: var(--accent-blue, #5b57d8); color: #fff;
      font-size: .72rem; display: inline-grid; place-items: center;
    }
    .people-filter-menu {
      position: absolute; top: calc(100% + 6px); inset-inline-start: 0; z-index: 40;
      width: min(320px, 92vw); max-height: min(420px, 70vh);
      display: flex; flex-direction: column;
      background: var(--panel-solid, var(--panel));
      border: 1px solid var(--line); border-radius: 10px;
      box-shadow: var(--shadow); overflow: hidden;
    }
    .people-filter-menu.hidden { display: none !important; }
    /* Two fields, the way Jira splits them: what is mine and what I asked
       for are different questions about the same board. */
    .people-filter-fields {
      display: flex; gap: 2px; padding: 6px 6px 0;
    }
    .people-filter-fields button {
      flex: 1 1 0; margin: 0; padding: 7px 8px; border: 0; cursor: pointer;
      background: transparent; color: var(--muted); font: inherit;
      font-size: var(--text-2xs); font-weight: 600; border-radius: 7px;
      display: inline-flex; align-items: center; justify-content: center; gap: 5px;
    }
    .people-filter-fields button:hover {
      background: color-mix(in srgb, var(--text) 7%, transparent);
    }
    .people-filter-fields button[aria-selected="true"] {
      background: color-mix(in srgb, var(--accent-blue) 14%, transparent);
      color: var(--text);
    }
    /* A field with people picked says so, even while the other tab is open. */
    .people-filter-fields button[data-picked]:not([data-picked=""])::after {
      content: attr(data-picked);
      min-width: 15px; padding: 0 4px; border-radius: 999px;
      background: var(--accent-blue); color: #fff;
      font-size: var(--text-3xs, .62rem); line-height: 15px;
    }

    .people-filter-search {
      margin: 10px 10px 6px; width: calc(100% - 20px); box-sizing: border-box;
      padding: 8px 10px; border-radius: 6px; border: 1px solid var(--line);
      background: var(--input); color: var(--text);
    }
    .people-filter-list {
      overflow: auto; padding: 4px 6px 8px; display: flex; flex-direction: column; gap: 2px;
    }
    .people-filter-item {
      display: flex; align-items: center; gap: 10px; width: 100%;
      padding: 8px 10px; border: 0; border-radius: 8px; cursor: pointer;
      background: transparent; color: var(--text); text-align: start;
    }
    .people-filter-item:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }
    .people-filter-item input { width: 16px; height: 16px; accent-color: var(--accent-blue, #5b57d8); flex: 0 0 auto; }
    .people-filter-avatar {
      width: 28px; height: 28px; border-radius: 999px; flex: 0 0 auto;
      display: grid; place-items: center; color: #fff; font-size: .68rem; font-weight: 600;
    }
    .people-filter-meta { min-width: 0; flex: 1; }
    .people-filter-name {
      font-weight: 600; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .people-filter-email {
      color: var(--muted); font-size: .75rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .people-filter-footer {
      display: flex; justify-content: space-between; gap: 8px; padding: 8px 10px;
      border-top: 1px solid var(--line);
    }
    .people-filter-footer .btn { width: auto; margin: 0; padding: 6px 10px; }
    .share-actions {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
      gap: 12px; margin: 14px 0;
    }
    .share-card {
      border: 1px solid var(--line); border-radius: 10px; padding: 14px;
      background: var(--panel-solid); cursor: pointer; text-align: start;
      color: inherit; width: 100%;
    }
    .share-card:hover { border-color: var(--accent-blue, var(--teal)); }
    .share-card strong { display: block; margin-bottom: 4px; }
    #membersList, #invitesList {
      border: 1px solid var(--line); border-radius: var(--radius-lg);
      background: var(--panel-solid); overflow: hidden;
    }
    .member-row {
      display: flex; gap: var(--space-3); align-items: center;
      justify-content: space-between; flex-wrap: wrap;
      padding: 9px var(--space-3); border: 0;
      border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
      background: transparent;
      transition: background var(--dur-fast) var(--ease-out);
    }
    .member-row:first-child { border-top: 0; }
    .member-row:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
    .member-row select { padding: 6px 10px; font-size: var(--text-sm); }
    /* Removing somebody is rare and irreversible. It does not need to shout
       from every row - only from the one you are about to press. */
    .member-row .btn.danger {
      border-color: transparent; color: var(--muted); font-weight: 450;
    }
    .member-row:hover .btn.danger { color: var(--danger); }
    .member-row .btn.danger:hover {
      border-color: color-mix(in srgb, var(--danger) 45%, transparent);
      background: color-mix(in srgb, var(--danger) 8%, transparent);
      color: var(--danger);
    }
    .member-row .member-main { display: flex; gap: 10px; align-items: center; min-width: 0; }
    .auth-links {
      display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap;
      margin-top: 14px; font-size: .9rem;
    }
    .auth-links a, .auth-links button.linkish {
      background: none; border: 0; color: var(--accent-blue, var(--teal));
      cursor: pointer; padding: 0; font-weight: 600; width: auto; margin: 0;
    }
    .oauth-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0 0 10px; }
    .oauth-btn { width: 100%; margin: 0; cursor: pointer; }
    .oauth-btn:disabled { opacity: .45; cursor: not-allowed; }
    .oauth-divider { text-align: center; font-size: .8rem; margin: 4px 0 12px; }
    .bot-check {
      display: flex !important; align-items: center; gap: 10px; margin: 10px 0 14px;
      font-size: .9rem; color: var(--text); cursor: pointer; user-select: none;
    }
    .bot-check input { width: auto; accent-color: var(--teal); }
    .captcha-box { margin: 8px 0 12px; min-height: 0; }
    .captcha-box:empty { display: none; }
    .hp-field {
      position: absolute !important; left: -9999px !important; width: 1px !important; height: 1px !important;
      opacity: 0 !important; pointer-events: none !important;
    }
    .col-head {
      margin: 0 0 8px; font-size: .8rem; display: flex; align-items: center; gap: 6px;
      justify-content: space-between; position: relative; text-transform: uppercase;
      letter-spacing: .03em; color: var(--muted); font-weight: 600;
    }
    .col-head-main { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; }
    .col-drag { cursor: grab; color: var(--muted); padding: 2px 4px; user-select: none; }
    .col-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); text-transform: none; letter-spacing: 0; font-size: .88rem; }
    .col-name[contenteditable="true"] { outline: 1px solid var(--teal); border-radius: 4px; padding: 0 4px; }
    .col-count {
      font-style: normal; color: var(--muted); font-weight: 600; flex-shrink: 0;
      background: color-mix(in srgb, var(--text) 10%, transparent); border-radius: 999px;
      min-width: 20px; height: 20px; display: grid; place-items: center; font-size: .72rem; padding: 0 6px;
    }
    .col-menu-btn {
      background: transparent; border: 0; color: var(--muted); cursor: pointer;
      padding: 2px 6px; border-radius: 6px; font-size: 1rem; line-height: 1;
    }
    .col-menu-btn:hover { color: var(--text); background: color-mix(in srgb, var(--teal) 12%, transparent); }
    .col-menu {
      position: absolute; inset-inline-end: 0; top: 28px; z-index: 6;
      background: var(--panel-solid); border: 1px solid var(--line); border-radius: 10px;
      box-shadow: var(--shadow); min-width: 140px; padding: 4px; display: none;
    }
    .col-menu.open { display: block; }
    .col-menu button {
      display: block; width: 100%; text-align: start; background: transparent; border: 0;
      color: var(--text); padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: .85rem;
    }
    .col-menu button:hover { background: color-mix(in srgb, var(--teal) 12%, transparent); }
    .col-menu button.danger { color: var(--danger); }
    .card {
      background: var(--card-bg, var(--panel-solid));
      border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
      border-radius: var(--radius-lg);
      padding: 10px 11px 9px; margin-bottom: 6px; cursor: grab;
      box-shadow: var(--elev-1);
      transition: box-shadow var(--dur-fast) var(--ease-out),
                  border-color var(--dur-fast) var(--ease-out);
      animation: fadeUp .25s ease;
    }
    /* A card lifts on hover instead of changing colour. Colour on a card
       already means something here - type, priority, who has it - and a
       hover tint is one more thing competing to be read. */
    .card:hover {
      border-color: color-mix(in srgb, var(--text) 16%, transparent);
      box-shadow: var(--elev-2); transform: none;
    }
    .card.dragging { opacity: .4; }
    /* The title is the only thing worth reading at board zoom, so it gets the
       weight and the room. Three lines then ellipsis - a 200-character summary
       must not turn one card into half a column. */
    .card .title {
      margin: 0 0 8px; font-weight: 500; line-height: 1.38; font-size: .875rem;
      color: var(--text); overflow-wrap: anywhere;
      display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
      overflow: hidden;
    }
    .card .card-foot {
      display: flex; align-items: center; justify-content: space-between; gap: 8px;
      /* No wrapping: the footer is one quiet line under the title, always. */
      flex-wrap: nowrap; min-width: 0;
    }
    .card .key {
      color: var(--muted); font-size: .7rem; font-weight: 600; letter-spacing: .01em;
      display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0;
    }
    /* The square in front of the key says what kind of work this is - it used
       to be green for everything, which is a legend that lies. */
    .card .key::before {
      content: ""; width: 12px; height: 12px; border-radius: 2px;
      background: var(--type-color, #4bce97); display: inline-block; flex-shrink: 0;
    }
    .card .key.type-story { --type-color: var(--tone-good-ink); }
    .card .key.type-task { --type-color: var(--tone-cool-ink); }
    .card .key.type-bug { --type-color: var(--tone-hot-ink); }
    .card .key.type-subtask { --type-color: var(--tone-idea-ink); }
    .meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; min-width: 0; }
    .card .meta { flex-wrap: nowrap; }

    /* Priority: a glyph in the priority's colour, no pill and no shouting. */
    .prio {
      font-size: .8rem; font-weight: 600; line-height: 1; flex-shrink: 0;
      display: inline-grid; place-items: center; width: 16px; height: 16px;
      color: var(--muted);
    }
    .prio-highest, .prio-urgent { color: var(--tone-hot-ink); }
    .prio-high { color: var(--tone-hot-ink); opacity: .8; }
    .prio-medium { color: var(--muted); }
    .prio-low { color: var(--tone-good-ink); opacity: .85; }
    .prio-lowest { color: var(--tone-good-ink); }

    /* Story points read as a count, so they get a count's shape. */
    .points {
      font-size: .68rem; font-weight: 600; color: var(--muted); flex-shrink: 0;
      min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
      display: inline-grid; place-items: center;
      background: color-mix(in srgb, var(--text) 10%, transparent);
    }

    /* A chip is a label, not an alarm. Filled and borderless in a soft tone
       reads at a glance without the outline shouting alongside it. */
    .chip {
      font-size: var(--text-2xs); padding: 2px 8px; border-radius: var(--radius-pill);
      border: 0; color: var(--muted);
      background: color-mix(in srgb, var(--text) 8%, transparent);
      font-weight: 500; letter-spacing: .02em;
    }
    .chip.high, .chip.highest { color: var(--tone-hot-ink); background: var(--tone-hot-bg); }
    .chip.medium { color: var(--tone-warm-ink); background: var(--tone-warm-bg); }
    .chip.low, .chip.lowest { color: var(--tone-good-ink); background: var(--tone-good-bg); }
    .avatar-chip {
      width: 24px; height: 24px; border-radius: 999px; background: #6554c0; color: #fff;
      font-size: .65rem; font-weight: 600; display: grid; place-items: center; flex-shrink: 0;
    }
    .avatar-chip.is-unassigned {
      background: transparent; color: var(--muted);
      border: 1px dashed color-mix(in srgb, var(--text) 28%, transparent);
    }
    .avatar-chip.is-unassigned:hover {
      color: var(--text); border-color: color-mix(in srgb, var(--text) 45%, transparent);
    }
    /* Backlog rows are one line: key, title, then the same quiet metadata a
       board card carries, right-aligned so the eye can scan down a column. */
    .backlog-row {
      display: flex; align-items: center; gap: 10px; min-width: 0;
    }
    .backlog-row .key {
      color: var(--muted); font-size: .72rem; font-weight: 600; letter-spacing: .01em;
      display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
    }
    .backlog-row .key::before {
      content: ""; width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0;
      background: var(--type-color, #4bce97); display: inline-block;
    }
    .backlog-row .key.type-story { --type-color: var(--tone-good-ink); }
    .backlog-row .key.type-task { --type-color: var(--tone-cool-ink); }
    .backlog-row .key.type-bug { --type-color: var(--tone-hot-ink); }
    .backlog-row .key.type-subtask { --type-color: var(--tone-idea-ink); }
    .backlog-title {
      flex: 1 1 auto; min-width: 0; font-size: .9rem; font-weight: 500;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .backlog-meta {
      display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
    }

    .list {
      margin: 0; padding: 0;
      border: 1px solid var(--line); border-radius: var(--radius-lg);
      background: var(--panel-solid); overflow: hidden;
    }
    .list li {
      list-style: none; margin: 0; padding: 9px var(--space-3);
      border: 0; border-top: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
      border-radius: 0; background: transparent; cursor: pointer;
      transition: background var(--dur-fast) var(--ease-out);
    }
    .list li:first-child { border-top: 0; }
    .list li:hover {
      transform: none;
      background: color-mix(in srgb, var(--text) 4%, transparent);
    }

    /* ===== Team calendar: 7 equal days, Sat fully visible ===== */
    #tab-calendar.panel {
      overflow: visible;
      max-width: 100%;
      width: 100%;
      box-sizing: border-box;
      padding: 12px;
    }
    .cal-layout {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr);
      flex: 1;
      min-height: 0;
      gap: 12px;
      width: 100%;
      max-width: 100%;
      min-width: 0;
      font-weight: 600;
      box-sizing: border-box;
      /* stretch, not start: the calendar must fill the row track it was given,
         otherwise it sizes to its content and pushes the page into a scroll */
      align-items: stretch;
      direction: ltr; /* keep column math stable; sidebar side set below */
    }
    .cal-main { grid-column: 1; grid-row: 1; min-width: 0; min-height: 0; }
    .cal-sidebar {
      border: 1px solid var(--line);
      border-radius: 12px;
      background: var(--panel-solid);
      padding: 12px;
      display: flex;
      flex-direction: column;
      gap: 14px;
      align-items: stretch;
      min-width: 0;
      width: 100%;
      max-width: 100%;
      box-sizing: border-box;
      position: sticky;
      top: 64px;
    }
    .cal-side-block { width: 100%; max-width: 100%; }
    .cal-side-section { min-width: 0; width: 100%; }
    .cal-tip { max-width: none; }
    /* Week days stay Sun→Sat even in Arabic */
    .cal-grid, .mini-cal-grid, .cal-sheet { direction: ltr; }

    /* Month picker docked in the app sidebar (calendar tabs only). */
    .app-side-cal {
      margin: 10px 0 4px;
      padding: 10px;
      border: 1px solid var(--line);
      border-radius: 10px;
      background: color-mix(in srgb, var(--panel-solid) 70%, transparent);
    }
    .app-side-cal .mini-cal-title { font-size: .8rem; margin-bottom: 6px; }
    .app-side-cal .mini-cal-title .btn {
      width: auto; margin: 0; padding: 2px 8px; font-size: .9rem; line-height: 1;
    }
    .app-side-cal-legend { display: grid; gap: 6px; margin-top: 10px; }
    .app-side-cal-legend .cal-legend-item { font-size: .76rem; }
    .shell.side-collapsed .app-side-cal { display: none; }
    .mini-cal-title {
      display: flex; align-items: center; justify-content: space-between;
      font-size: .85rem; font-weight: 600; margin-bottom: 8px;
    }
    .mini-cal-grid {
      display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; text-align: center;
      width: 100%;
    }
    .mini-cal-grid .mh { font-size: .62rem; color: var(--muted); font-weight: 600; padding: 4px 0; }
    .mini-cal-grid .md {
      font-size: .72rem; font-weight: 600; padding: 6px 0; border-radius: 999px; cursor: pointer;
      color: var(--text);
    }
    .mini-cal-grid .md.out { opacity: .35; }
    .mini-cal-grid .md.today { background: #3b82f6; color: #fff; }
    .mini-cal-grid .md:hover:not(.out) { background: color-mix(in srgb, #3b82f6 18%, transparent); }
    .cal-side-section h4 {
      margin: 0 0 8px; font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;
      color: var(--muted); font-weight: 600;
    }
    .cal-legend { display: grid; gap: 6px; }
    .cal-legend-item {
      display: flex; align-items: center; gap: 8px; font-size: .8rem; font-weight: 600;
    }
    .cal-legend-dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }

    /* Meetings calendar reuses the board calendar's surface; only the
       sidebar scope toggle needs its own stacking. */
    .cal-views.cal-views-stack {
      display: grid; grid-template-columns: 1fr 1fr; width: 100%;
    }
    .cal-views.cal-views-stack button { width: 100%; }

    .cal-main {
      min-width: 0;
      width: 100%;
      max-width: 100%;
      display: flex;
      flex-direction: column;
      gap: 8px;
      box-sizing: border-box;
    }
    .cal-toolbar {
      display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px;
      width: 100%;
    }
    .cal-toolbar-left, .cal-toolbar-right {
      display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    }
    .cal-month-title {
      font-size: 1.05rem; font-weight: 600; text-align: center;
    }
    .cal-views { display: flex; gap: 4px; flex-wrap: wrap; }
    .cal-views button {
      border: 1px solid var(--line); background: transparent; color: var(--muted);
      padding: 6px 10px; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: .78rem;
    }
    .cal-views button.active {
      background: #3b82f6; border-color: #3b82f6; color: #fff;
    }
    .cal-sheet {
      border: 1px solid var(--line);
      border-radius: 10px;
      background: var(--panel-solid);
      width: 100%;
      max-width: 100%;
      min-width: 0;
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
      /* One scrollbar for the whole grid, only when the month genuinely
         needs more room than the screen has. */
      overflow-y: auto;
      overflow-x: hidden;
      scrollbar-width: thin;
      scrollbar-color: color-mix(in srgb, var(--text) 22%, transparent) transparent;
      box-sizing: border-box;
    }
    .cal-sheet::-webkit-scrollbar { width: 8px; }
    .cal-sheet::-webkit-scrollbar-thumb {
      background: color-mix(in srgb, var(--text) 18%, transparent); border-radius: 999px;
    }
    .cal-grid-head {
      flex: 0 0 auto;
      position: sticky; top: 0; z-index: 2;
    }
    .cal-grid {
      display: grid;
      grid-template-columns: repeat(7, minmax(0, 1fr));
      gap: 0;
      width: 100%;
      min-width: 0;
      box-sizing: border-box;
    }
    .cal-grid-body {
      align-items: stretch;
      /* Deliberately NOT flex:1 — a definite height would cap the `auto` rows
         and events would spill out of their cells instead of growing them. */
      flex: 0 0 auto;
      height: auto;
    }
    /* Month: a week row is as tall as its busiest day.
       Capping it and counting the overflow hid the only day worth reading,
       and scrolling inside a cell that size is a worse way to read a list
       than simply seeing it. So the row grows, the page takes the height,
       and every meeting is on screen - which is what a wall calendar does
       and what Teamup does. --week-min keeps a quiet week from collapsing. */
    .cal-grid-body[data-view="month"] {
      --week-min: 118px;
      grid-template-rows: repeat(6, minmax(var(--week-min), auto));
      overflow: visible;
    }
    /* Week and day are a single row, so let them use the full height. */
    .cal-grid-body[data-view="week"],
    .cal-grid-body[data-view="day"] {
      grid-template-rows: minmax(0, 1fr);
      flex: 1;
      min-height: 0;
      overflow: hidden;
    }
    .cal-head {
      text-align: center;
      color: var(--muted);
      font-size: .76rem;
      font-weight: 600;
      padding: 8px 2px;
      border-bottom: 1px solid var(--line);
      background: color-mix(in srgb, var(--panel-solid) 92%, var(--muted));
      text-transform: uppercase;
      letter-spacing: .02em;
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      position: sticky;
      top: 0;
      z-index: 2;
    }
    .day {
      min-height: 0;
      height: auto;
      align-self: stretch;
      min-width: 0 !important;
      width: auto;
      max-width: none;
      box-sizing: border-box;
      border-right: 1px solid var(--line);
      border-bottom: 1px solid var(--line);
      padding: 4px;
      background: transparent;
      cursor: pointer;
      border-radius: 0;
      overflow: visible;
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      align-items: stretch;
      gap: 2px;
      transition: background .12s;
    }
    .day .day-events {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      align-items: stretch;
      gap: 2px;
      width: 100%;
      /* Every meeting, stacked, no scroller. The cell grows and its week row
         grows with it. */
      flex: 0 0 auto;
      overflow: visible;
    }

    .day .n { flex: 0 0 auto; }
    /* Drag-to-reschedule affordances */
    .evt[draggable="true"]:active { cursor: grabbing; }
    .evt.evt-dragging { opacity: .4; }
    .day.day-drop-target {
      background: color-mix(in srgb, var(--accent-blue) 16%, transparent) !important;
      box-shadow: inset 0 0 0 2px var(--accent-blue);
    }
    .day:nth-child(7n) { border-right: none; }
    .day:hover { background: color-mix(in srgb, var(--accent-blue) 5%, transparent); }
    .day.out { opacity: .45; background: color-mix(in srgb, var(--muted) 6%, transparent); }
    .day.today {
      background: color-mix(in srgb, var(--accent-blue) 7%, transparent);
      box-shadow: inset 0 0 0 2px var(--accent-blue);
    }
    [data-theme="dark"] .day.today,
    [data-theme="ocean"] .day.today,
    [data-theme="forest"] .day.today,
    [data-theme="sunset"] .day.today {
      background: color-mix(in srgb, #3b82f6 18%, var(--panel-solid));
    }
    .day .n {
      font-size: .8rem;
      color: var(--text);
      margin-bottom: 4px;
      display: flex;
      justify-content: flex-end;
      align-items: center;
      gap: 4px;
      font-weight: 600;
    }
    html[dir="rtl"] .day .n { justify-content: flex-start; }
    .day-num {
      width: 28px; height: 28px; display: grid; place-items: center;
      border-radius: 999px; font-weight: 600; font-size: .86rem;
    }
    .day.today .day-num {
      background: #3b82f6;
      color: #fff;
    }
    .today-label { display: none; }
    /* A chip reads "time, then what": the time is what you scan a column for,
       so it leads and stays legible while the title truncates. */
    .evt {
      display: flex;
      align-items: baseline;
      gap: 5px;
      width: 100%;
      max-width: 100%;
      box-sizing: border-box;
      font-size: var(--text-2xs);
      font-weight: 500;
      padding: 2px 6px 2px 8px;
      border-radius: var(--radius-sm);
      margin: 0;
      overflow: hidden;
      background: color-mix(in srgb, var(--evt-color, #3B82F6) 15%, var(--panel-solid));
      color: var(--text) !important;
      box-shadow: inset 2px 0 0 var(--evt-color, #3B82F6);
      cursor: grab;
      line-height: 1.45;
      flex-shrink: 0;
    }
    .evt-time {
      font-weight: 600; font-variant-numeric: tabular-nums;
      flex: 0 0 auto; color: var(--muted);
    }
    .evt-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .evt:hover {
      background: color-mix(in srgb, var(--evt-color, #3B82F6) 26%, var(--panel-solid));
    }
    /* A deadline already past is the only thing on this grid worth
       interrupting someone about, so it is the only state with its own look. */
    .evt-overdue {
      background: var(--danger);
      color: #fff !important;
      box-shadow: inset 2px 0 0 rgba(255, 255, 255, .45);
    }
    .evt-overdue .evt-time { color: rgba(255, 255, 255, .8); }
    .evt-overdue:hover { background: var(--danger); filter: brightness(1.08); }
    /* Overflow for a busy day, instead of a row that stretches to fit it. */
    .cal-more {
      font-size: var(--text-2xs); font-weight: 500; color: var(--accent-blue); padding: 2px;
    }
    .color-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .color-row input[type="color"] { width: 48px; height: 36px; padding: 2px; cursor: pointer; }
    .color-presets { display: flex; gap: 6px; flex-wrap: wrap; }
    .color-preset {
      width: 22px; height: 22px; border-radius: 999px; border: 2px solid var(--line);
      cursor: pointer; padding: 0;
    }
    .color-preset.active { border-color: var(--text); }
    .attach-section { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }

    /* ===== Speak your tasks =====
       One button in the header. Recording is the only state loud enough to
       interrupt what is behind it, so it is the only one that pulses. */
    .mic-btn {
      width: 34px; height: 34px; margin: 0; padding: 0; border-radius: 999px;
      display: grid; place-items: center; flex: 0 0 auto;
    }
    .mic-glyph { font-size: 1rem; line-height: 1; }
    .mic-btn.is-recording {
      background: #d93f3c; border-color: #d93f3c; color: #fff;
      animation: micPulse 1.3s ease-in-out infinite;
    }
    .mic-btn.is-busy { opacity: .6; cursor: progress; }
    @keyframes micPulse {
      0%, 100% { box-shadow: 0 0 0 0 rgba(217, 63, 60, .55); }
      50% { box-shadow: 0 0 0 7px rgba(217, 63, 60, 0); }
    }
    @media (prefers-reduced-motion: reduce) {
      .mic-btn.is-recording { animation: none; }
    }

    /* A whole-team action sits apart from the one-at-a-time invite box, so
       nobody presses it looking for the ordinary one. */
    .onboard-box {
      display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
      margin: 18px 0 0; padding: 14px 16px;
      border: 1px dashed var(--line); border-radius: 12px;
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 6%, transparent);
    }
    .onboard-box > div { flex: 1 1 260px; min-width: 0; }
    .onboard-box b { font-size: .9rem; font-weight: 600; }
    .onboard-box p { margin: 3px 0 0; font-size: .8rem; line-height: 1.5; }
    .onboard-box .btn { width: auto; margin: 0; flex: 0 0 auto; }

    /* A date input renders its numerals in the page's language, so under
       Arabic every due date came out as ٠٨/٠٩/٢٠٢٦ - which is correct, and
       also unreadable beside an issue key and a Latin-numbered board. Dates
       and numbers stay Latin; the labels around them stay Arabic. */
    input[type="date"], input[type="time"], input[type="number"],
    .points, .card .key, .backlog-row .key, .dash-row .key,
    .evt-time, .frame-url {
      font-variant-numeric: lining-nums tabular-nums;
      -moz-font-feature-settings: "lnum";
      font-feature-settings: "lnum";
    }
    html[dir="rtl"] input[type="date"],
    html[dir="rtl"] input[type="time"],
    html[dir="rtl"] input[type="number"] {
      direction: ltr; text-align: start;
    }

    /* Project and column side by side: together they are one answer to
       "where does this land", so they read as one row. */
    .vr-where {
      display: grid; gap: var(--space-3);
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    }

    /* ===== The details pane =====
       It was a form: a label, then a full-width box, seven times down the
       side of the dialog. But a task's properties are read far more often
       than they are changed - you open a task to see who has it and when it
       is due, and only sometimes to move it.

       So it reads as a list of facts. The name sits on the left, the value
       on the right, and the control keeps no box until you go near it. Same
       controls, same save path; what changed is that it stops shouting
       "fill me in" at somebody who came to read. */
    .modal-pane-side .issue-field {
      display: grid;
      /* The name is short and fixed; the value is a person's name or a date
         and needs the room. At 38% the assignee came out as "...mo User". */
      grid-template-columns: minmax(78px, 32%) minmax(0, 1fr);
      align-items: center;
      gap: var(--space-2);
      margin: 0;
      padding: 3px 0;
      min-height: 34px;
    }
    .modal-pane-side .issue-field > label {
      margin: 0;
      font-size: var(--text-xs);
      font-weight: 450;
      color: var(--muted);
      align-self: center;
    }
    /* The label carries its own shortcut ("Assign to me"), which belongs
       beside the value it changes rather than above the pair. */
    .modal-pane-side .issue-field > label.with-action {
      grid-column: 1 / -1;
      justify-content: space-between;
      margin-bottom: -6px;
    }
    /* ...and so does its value. The label spanning the row left the control
       to fall into the narrow label column, where Assignee was squeezed to a
       third of the width every other field got - unreadable, and the only
       field in the panel you cannot read is the one naming a person. */
    .modal-pane-side .issue-field > label.with-action ~ * {
      grid-column: 1 / -1;
    }

    .modal-pane-side .issue-field select,
    .modal-pane-side .issue-field input,
    .modal-pane-side .people-pick-trigger {
      border-color: transparent;
      background: transparent;
      padding: 5px 8px;
      min-height: 30px;
      font-size: var(--text-sm);
      transition: background var(--dur-fast) var(--ease-out),
                  border-color var(--dur-fast) var(--ease-out);
    }
    .modal-pane-side .issue-field select:hover,
    .modal-pane-side .issue-field input:hover,
    .modal-pane-side .people-pick-trigger:hover {
      background: color-mix(in srgb, var(--text) 7%, transparent);
    }
    .modal-pane-side .issue-field select:focus,
    .modal-pane-side .issue-field input:focus {
      background: var(--input);
      border-color: var(--accent-blue, var(--teal));
    }
    .modal-pane-side .people-pick-trigger { gap: 6px; }
    .modal-pane-side .people-pick-trigger .avatar-chip { width: 18px; height: 18px; font-size: .55rem; }
    /* Labels and the "add" row keep the full width - they are a list, not a
       single value. */
    .modal-pane-side .issue-field-labels,
    .modal-pane-side .label-row { grid-template-columns: 1fr; }

    /* A hairline between facts, the way a table of properties reads. */
    .modal-pane-side .issue-field + .issue-field {
      border-top: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
    }

    /* When and by whom. Jira puts this at the foot of the panel and it is
       the first thing anybody asks about a task they did not write. */
    .detail-stamps {
      margin-top: var(--space-4);
      padding-top: var(--space-3);
      border-top: 1px solid var(--line);
      display: grid; gap: 4px;
      font-size: var(--text-2xs); color: var(--muted);
    }
    .detail-stamps div { display: flex; justify-content: space-between; gap: 10px; }
    .detail-stamps b { font-weight: 450; color: var(--text); }

    @media (max-width: 860px) {
      /* Stacked, the two-column row would leave a stripe of empty label. */
      .modal-body.split .modal-pane-side .issue-field {
        grid-template-columns: minmax(84px, 34%) minmax(0, 1fr);
      }
    }

    /* ===== Formatting a description =====
       The toolbar sits inside the composer's border, above the text, so the
       whole thing still reads as one field rather than a widget with a strip
       bolted on top. */
    .desc-tools {
      display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
      padding: 5px 6px; border-bottom: 1px solid var(--line);
      background: color-mix(in srgb, var(--text) 3%, transparent);
      position: relative;   /* the colour menu hangs off this */
    }
    .desc-tool {
      min-width: 28px; height: 28px; padding: 0 7px; border: 0;
      border-radius: 7px; cursor: pointer; background: transparent;
      color: var(--muted); font: inherit; font-size: var(--text-xs);
      line-height: 1; display: inline-grid; place-items: center;
    }
    .desc-tool:hover { background: color-mix(in srgb, var(--text) 9%, transparent); color: var(--text); }
    /* LTR and RTL are words, not glyphs, and they have to read as words. */
    .desc-tool[data-dir] {
      font-size: var(--text-2xs); font-weight: 700; letter-spacing: .04em;
      padding: 0 6px; direction: ltr;
    }
    .desc-tool[aria-pressed="true"] {
      background: color-mix(in srgb, var(--accent-blue, #0c66e4) 16%, transparent);
      color: var(--text);
    }
    /* A line that has been turned over reads to its own edge, not the box’s. */
    .desc-editor [dir="rtl"] { text-align: right; }
    .desc-editor [dir="ltr"] { text-align: left; }
    .desc-tool-colour { position: relative; font-weight: 700; }
    /* The letter carries a bar underneath in the colour it will apply, which
       is how every word processor has drawn this control for thirty years. */
    .desc-tool-colour::after {
      content: ""; position: absolute; left: 6px; right: 6px; bottom: 4px;
      height: 2px; border-radius: 2px;
      background: linear-gradient(90deg, #e5484d, #d9730d, #2f8a4c, #0c66e4);
    }
    .desc-colour-menu {
      position: absolute; top: calc(100% + 6px); inset-inline-start: 0; z-index: 60;
      display: grid; grid-template-columns: repeat(4, 22px); gap: 6px;
      padding: 8px; border-radius: 9px;
      background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: 0 12px 30px rgba(0,0,0,.24);
    }
    .desc-colour-dot {
      width: 22px; height: 22px; margin: 0; padding: 0; cursor: pointer;
      border: 1px solid color-mix(in srgb, var(--text) 18%, transparent);
      border-radius: 6px;
    }
    .desc-colour-dot:hover { transform: scale(1.12); }
    .desc-colour-dot.is-none {
      background: transparent; position: relative;
    }
    /* "Default" is the absence of a colour, drawn as one struck through. */
    .desc-colour-dot.is-none::after {
      content: ""; position: absolute; inset: 0; margin: auto;
      width: 1px; height: 26px; background: #e5484d;
      transform: rotate(45deg);
    }

    .desc-tools-sep {
      width: 1px; height: 16px; margin: 0 4px;
      background: var(--line); flex: 0 0 auto;
    }

    /* What the marks look like while you are writing them. The editor is the
       preview: there is no second pane to keep in step. */
    .desc-editor h2, .desc-editor h3, .desc-editor h4 {
      margin: 10px 0 4px; font-weight: 600; line-height: 1.3;
    }
    .desc-editor h2 { font-size: var(--text-lg); }
    .desc-editor h3 { font-size: var(--text-base); }
    .desc-editor h4 { font-size: var(--text-sm); }
    .desc-editor blockquote {
      margin: 6px 0; padding: 2px 0 2px 12px;
      border-inline-start: 3px solid color-mix(in srgb, var(--accent-blue) 45%, transparent);
      color: var(--muted);
    }
    .desc-editor code {
      font-family: var(--mono, ui-monospace, monospace);
      font-size: .92em; padding: 1px 5px; border-radius: 5px;
      background: color-mix(in srgb, var(--text) 9%, transparent);
    }
    .desc-editor a { color: var(--accent-blue); text-decoration: underline; text-underline-offset: 2px; }
    .desc-editor s { opacity: .7; }
    .desc-editor hr {
      border: 0; border-top: 1px solid var(--line); margin: 10px 0;
    }

    /* The people filter is a control, and it was taking the width of the
       board's whole toolbar: a Filter button, then All, then Unassigned,
       then eight faces, then a +16. Six faces is enough to recognise a team
       at a glance; the rest are one press away in the menu that already
       exists for exactly that. */
    .assignee-filter-btn { width: 24px; height: 24px; }
    .board-bar-tools { gap: 6px; }

    /* Two rows of the account menu used a font character for their icon -
       U+23FB and U+229E - and Windows has no glyph for either, so they came
       out as empty boxes that read as a broken image rather than a button.
       Drawn instead, which cannot be missing. */
    .account-ico { width: 16px; height: 16px; flex: 0 0 auto; }

    /* ===== Mentioning somebody =====
       Positioned against the document rather than the field, so it is not
       clipped by the dialog's own scrolling. */
    .mention-menu {
      position: absolute; z-index: 60; width: min(300px, 90vw);
      padding: 6px; border-radius: var(--radius-lg);
      background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: var(--elev-3);
      max-height: 260px; overflow-y: auto;
    }
    .mention-menu.hidden { display: none; }
    .mention-row {
      display: flex; align-items: center; gap: 9px; width: 100%;
      padding: 7px 8px; border: 0; border-radius: 8px; cursor: pointer;
      background: transparent; color: var(--text); font: inherit;
      font-size: var(--text-sm); text-align: start;
    }
    .mention-row:hover,
    .mention-row.on { background: color-mix(in srgb, var(--accent-blue) 12%, transparent); }
    .mention-who { display: grid; min-width: 0; line-height: 1.3; }
    .mention-who .muted { font-size: var(--text-2xs); overflow: hidden;
      text-overflow: ellipsis; white-space: nowrap; }
    .mention-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .mention-empty { margin: 8px; font-size: var(--text-sm); }

    /* ===== The AI launcher =====
       Fixed to a corner so it is in the same place on every screen, above
       the board's own scrolling but below any dialog it opens. */
    .launcher {
      position: fixed; z-index: 25;
      inset-inline-end: 20px;
      bottom: calc(20px + env(safe-area-inset-bottom, 0px));
      display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
    }
    .launcher.hidden { display: none; }

    .launcher-fab {
      width: 52px; height: 52px; border-radius: var(--radius-pill);
      border: 0; cursor: pointer; display: grid; place-items: center;
      background: var(--accent-blue, #5b57d8); color: #fff; font-size: 1.35rem;
      box-shadow: 0 6px 16px -6px rgba(91, 87, 216, .7), var(--elev-2);
      transition: transform var(--dur-base) var(--ease-out),
                  box-shadow var(--dur-base) var(--ease-out);
    }
    .launcher-fab:hover { transform: translateY(-2px); }
    .launcher.is-open .launcher-fab { transform: rotate(45deg); }
    .launcher-fab-glyph { line-height: 1; }

    .launcher-menu {
      display: grid; gap: 4px; width: min(300px, calc(100vw - 40px));
      padding: 7px; border-radius: 16px;
      background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: var(--elev-3);
      transform-origin: bottom right;
      opacity: 0; transform: translateY(8px) scale(.96); pointer-events: none;
      transition: opacity var(--dur-base) var(--ease-out),
                  transform var(--dur-base) var(--ease-out);
    }
    .launcher.is-open .launcher-menu {
      opacity: 1; transform: none; pointer-events: auto;
    }
    .launcher-item {
      display: flex; align-items: flex-start; gap: 11px; width: 100%;
      padding: 10px; border: 0; border-radius: 11px; cursor: pointer;
      background: transparent; color: var(--text); font: inherit;
      text-align: start;
    }
    .launcher-item:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
    .launcher-item b { display: block; font-size: var(--text-sm); font-weight: 500; }
    .launcher-item small { display: block; font-size: var(--text-2xs); line-height: 1.45; }
    .launcher-glyph {
      flex: 0 0 auto; width: 30px; height: 30px; border-radius: 9px;
      display: grid; place-items: center; font-size: .95rem;
      background: color-mix(in srgb, var(--accent-blue) 14%, transparent);
    }

    @media (prefers-reduced-motion: reduce) {
      .launcher-fab, .launcher-menu { transition: none; }
      .launcher.is-open .launcher-fab { transform: none; }
    }

    /* ===== Picking a person =====
       The native select is still there, holding the value; it is only ever
       hidden, never removed, so the dialog's save path is untouched. */
    .people-native { display: none !important; }
    .people-pick { position: relative; }
    .people-pick-trigger {
      width: 100%; display: flex; align-items: center; gap: 7px;
      padding: 7px 10px; cursor: pointer; text-align: start;
      border: 1px solid var(--line); border-radius: 9px;
      background: var(--input); color: var(--text);
      font: inherit; font-size: var(--text-sm); min-height: 38px;
    }
    .people-pick-trigger:hover { border-color: color-mix(in srgb, var(--text) 26%, transparent); }
    .people-pick-name {
      flex: 1 1 auto; min-width: 0; overflow: hidden;
      text-overflow: ellipsis; white-space: nowrap;
    }
    .people-pick-trigger > .muted { flex: 1 1 auto; }
    .people-pick-caret { flex: 0 0 auto; color: var(--muted); font-size: .8rem; }
    .avatar-chip.sm { width: 20px; height: 20px; font-size: .58rem; }
    /* Faces overlap slightly, the way a group of people does on any board
       that shows more than one. */
    .people-pick-trigger .avatar-chip.sm + .avatar-chip.sm { margin-inline-start: -8px; }

    .people-pick-menu {
      position: absolute; inset-inline: 0; top: calc(100% + 4px); z-index: 40;
      background: var(--panel-solid); border: 1px solid var(--line);
      border-radius: var(--radius-lg); box-shadow: var(--elev-3);
      padding: 8px; display: grid; gap: 6px;
    }
    .people-pick-search { font-size: var(--text-sm); padding: 7px 10px; }
    .people-pick-list { max-height: 260px; overflow-y: auto; display: grid; gap: 1px; }
    .people-pick-row {
      display: flex; align-items: center; gap: 9px; width: 100%;
      padding: 7px 8px; border: 0; border-radius: 8px; cursor: pointer;
      background: transparent; color: var(--text); font: inherit;
      font-size: var(--text-sm); text-align: start;
    }
    .people-pick-row:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
    .people-pick-row.on { background: color-mix(in srgb, var(--accent-blue) 12%, transparent); }
    .people-pick-who { display: grid; min-width: 0; line-height: 1.3; }
    .people-pick-who .muted { font-size: var(--text-2xs); overflow: hidden;
      text-overflow: ellipsis; white-space: nowrap; }
    .people-pick-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .people-pick-tick { margin-inline-start: auto; color: var(--accent-blue); opacity: 0; }
    .people-pick-row.on .people-pick-tick { opacity: 1; }
    .people-pick-empty { margin: 10px 8px; font-size: var(--text-sm); }

    /* ===== The assistant =====
       A conversation about the board, so it gets the shape of a thread
       rather than a form: it grows downward, it scrolls, and the newest
       thing is the thing you are reading. */
    .modal.modal-assistant {
      width: min(680px, 100%); display: flex; flex-direction: column;
      max-height: min(84vh, 780px);
    }
    .assistant-thread {
      flex: 1 1 auto; min-height: 220px; overflow-y: auto;
      padding: 14px 16px; display: grid; gap: 12px; align-content: start;
    }
    .assistant-hello { margin: 0 0 4px; font-size: .86rem; line-height: 1.55; }
    .assistant-openers { display: grid; gap: 6px; justify-items: start; }
    .assistant-opener {
      margin: 0; padding: 7px 12px; border-radius: 999px; cursor: pointer;
      border: 1px solid var(--line); background: transparent;
      color: var(--text); font: inherit; font-size: .82rem; text-align: start;
    }
    .assistant-opener:hover {
      border-color: color-mix(in srgb, var(--accent-blue, #5b57d8) 55%, transparent);
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 10%, transparent);
    }
    /* Yours sits to the trailing edge and is tinted; its replies stay plain
       and full width, because the replies are the part you read carefully. */
    .assistant-turn { max-width: 92%; }
    .assistant-turn.is-mine { margin-inline-start: auto; }
    .assistant-turn.is-theirs { max-width: 100%; }
    .assistant-text {
      margin: 0; padding: 9px 12px; border-radius: 12px;
      font-size: .86rem; line-height: 1.6; white-space: pre-wrap;
      overflow-wrap: anywhere;
    }
    .assistant-turn.is-mine .assistant-text {
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 16%, transparent);
      border-radius: 12px 12px 3px 12px;
    }
    .assistant-turn.is-theirs .assistant-text {
      background: color-mix(in srgb, var(--text) 6%, transparent);
      border-radius: 12px 12px 12px 3px;
    }
    .assistant-proposals {
      display: grid; gap: 4px; margin: 8px 0 0; padding: 10px;
      border: 1px solid var(--line); border-radius: 10px;
    }
    .assistant-proposal {
      display: flex; gap: 9px; align-items: flex-start; margin: 0;
      padding: 6px; border-radius: 7px; cursor: pointer; font-weight: 400;
    }
    .assistant-proposal:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
    .assistant-proposal:has(.ap-include:not(:checked)) { opacity: .5; }
    .ap-include {
      width: 16px; height: 16px; margin: 2px 0 0; flex: 0 0 auto;
      accent-color: var(--accent-blue, #5b57d8);
    }
    .ap-body { display: grid; gap: 2px; min-width: 0; }
    .ap-body strong { font-size: .85rem; font-weight: 500; }
    .ap-meta { font-size: .73rem; }
    /* One line saying what on the board prompted this - the difference
       between a suggestion you can judge and one you take on faith. */
    .ap-why { font-size: .73rem; font-style: italic; }
    .ap-foot {
      display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap;
      margin-top: 6px;
    }
    .ap-project { display: grid; gap: 3px; margin: 0; flex: 1 1 200px; font-weight: 400; }
    .ap-project > span { font-size: .73rem; }
    .ap-project select { margin: 0; }
    .ap-foot .btn { width: auto; margin: 0; flex: 0 0 auto; }
    .ap-error:empty { display: none; }
    .ap-error { margin: 6px 0 0; font-size: .78rem; color: var(--danger, #d64545); }
    .assistant-ask {
      display: flex; gap: 8px; align-items: flex-end;
      padding: 12px 16px; border-top: 1px solid var(--line);
    }
    /* Two lines to start. The shared textarea rule is sized for a task
       description, which here would eat half the thread. */
    .assistant-ask textarea {
      margin: 0; flex: 1 1 auto; resize: vertical;
      min-height: 54px; max-height: 140px;
    }
    .assistant-ask .btn { width: auto; margin: 0; flex: 0 0 auto; }

    /* A live caption while it listens, so you can see it is hearing you
       before you commit to whatever it thinks you said. */
    .voice-live {
      position: fixed; inset-inline: 0; bottom: 22px; z-index: 60;
      margin-inline: auto; width: max-content; max-width: min(640px, 92vw);
      display: flex; align-items: center; gap: 12px;
      padding: 12px 14px; border-radius: 999px;
      background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: 0 14px 40px rgba(0, 0, 0, .3);
      animation: fadeUp .2s ease;
    }
    .voice-live-dot {
      width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
      background: #d93f3c; animation: micPulse 1.3s ease-in-out infinite;
    }
    .voice-live-text {
      font-size: .9rem; min-width: 0; overflow: hidden;
      text-overflow: ellipsis; white-space: nowrap;
    }
    /* Which language you are speaking, not which one the app is in. */
    .voice-live-langs {
      display: inline-flex; flex: 0 0 auto; border-radius: 999px;
      border: 1px solid var(--line); overflow: hidden;
    }
    .voice-lang {
      margin: 0; padding: 5px 11px; border: 0; background: transparent;
      color: var(--muted); font: inherit; font-size: .76rem; font-weight: 600;
      cursor: pointer;
    }
    .voice-lang:hover { color: var(--text); }
    .voice-lang.on {
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 20%, transparent);
      color: var(--text);
    }
    .voice-live-stop { width: auto; margin: 0; padding: 6px 14px; flex: 0 0 auto; }
    @media (prefers-reduced-motion: reduce) {
      .voice-live, .voice-live-dot { animation: none; }
    }

    /* The proposal. Every row is editable, because speech is ambiguous. */
    .modal.modal-voice { width: min(680px, 100%); }
    .vr-summary {
      margin: 0 0 14px; font-size: .92rem; line-height: 1.5; color: var(--text);
    }
    .vr-empty { margin: 18px 0; font-size: .9rem; }
    /* A sentence that asked for thinking, quoted back with the reason. */
    /* The slower, better answer arrived and replaced the first one. Said
       quietly: it is reassurance, not news. */
    .vr-improved {
      margin: 0 0 12px; font-size: .76rem; font-weight: 600;
      color: color-mix(in srgb, var(--accent-blue, #5b57d8) 80%, var(--text));
    }
    .vr-thinking { margin-bottom: 14px; }
    .vr-thinking strong { display: block; margin-bottom: 6px; }
    .vr-thinking-line {
      margin: 0 0 6px; padding: 6px 10px; border-radius: 5px;
      background: color-mix(in srgb, var(--text) 7%, transparent);
      font-size: .85rem; line-height: 1.5;
    }
    .vr-thinking-why { margin: 0; font-size: .78rem; line-height: 1.5; }
    .vr-list { display: grid; gap: 10px; margin: 12px 0; }
    .vr-task {
      display: flex; gap: 10px; align-items: flex-start;
      padding: 10px; border: 1px solid var(--line); border-radius: 8px;
      background: color-mix(in srgb, var(--panel-solid) 85%, transparent);
    }
    /* An unticked row stays readable but stops competing for attention. */
    .vr-task:has(.vr-include:not(:checked)) { opacity: .5; }
    .vr-keep { margin: 3px 0 0; }
    .vr-include { width: 17px; height: 17px; accent-color: var(--accent-blue, #5b57d8); }
    .vr-fields { flex: 1 1 auto; min-width: 0; display: grid; gap: 8px; }
    .vr-title { margin: 0; font-weight: 500; }
    .vr-row { display: flex; gap: 8px; flex-wrap: wrap; }
    .vr-row select, .vr-row input { margin: 0; width: auto; flex: 1 1 140px; min-width: 0; }
    .vr-note { font-size: .76rem; }
    /* The words come first and stay open: you are confirming a reading of
       what you said, and you cannot check a reading against a transcript
       hidden behind a disclosure triangle. */
    .vr-said { margin: 0 0 14px; }
    .vr-said-head {
      display: flex; align-items: center; gap: 8px; margin-bottom: 5px;
    }
    .vr-said .transcript-copy { margin-inline-start: auto; }
    .vr-section-title {
      margin: 18px 0 8px; font-size: .74rem; font-weight: 600;
      letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
    }
    .vr-meeting { border-inline-start: 3px solid #0EA5E9; }

    /* ===== Voice notes =====
       A recording that can be played and read. */
    .attach-audio {
      display: block; width: 100%; max-width: 320px; height: 32px; margin: 8px 0 0;
    }
    .attach-item-audio { align-items: flex-start; }
    .transcript { margin: 8px 0 0; }
    .transcript-head {
      display: flex; align-items: center; gap: 8px; margin-bottom: 4px;
    }
    .transcript-label {
      font-size: .68rem; font-weight: 600; letter-spacing: .06em;
      text-transform: uppercase; color: var(--muted);
    }
    .transcript-meta { font-size: .7rem; }
    .transcript-copy, .transcript-run {
      width: auto; margin: 0; padding: 3px 8px; font-size: .72rem;
    }
    .transcript-copy { margin-inline-start: auto; }
    /* The recogniser is good, not perfect - the text is quoted rather than
       stated, so it reads as "what was heard", not as the record itself. */
    .transcript-text {
      margin: 0; padding: 8px 10px; border-radius: 6px;
      border-inline-start: 3px solid color-mix(in srgb, var(--accent-blue, #5b57d8) 55%, transparent);
      background: color-mix(in srgb, var(--text) 5%, transparent);
      font-size: .84rem; line-height: 1.55; white-space: pre-wrap;
      overflow-wrap: anywhere;
    }
    .transcript-working { display: flex; align-items: center; gap: 8px; font-size: .8rem; }
    .transcript-spinner {
      width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto;
      border: 2px solid color-mix(in srgb, var(--text) 20%, transparent);
      border-top-color: var(--accent-blue, #5b57d8);
      animation: spin .7s linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }
    @media (prefers-reduced-motion: reduce) {
      .transcript-spinner { animation: none; border-top-color: var(--accent-blue, #5b57d8); }
    }
    .transcript-failed { font-size: .8rem; display: grid; gap: 6px; justify-items: start; }
    .attach-list { display: grid; gap: 10px; margin: 10px 0; }
    .attach-item {
      display: flex; align-items: center; gap: 12px; padding: 10px; border: 1px solid var(--line);
      border-radius: 10px; background: color-mix(in srgb, var(--panel-solid) 80%, transparent);
    }
    .attach-item img {
      width: 112px; height: 84px; object-fit: cover; border-radius: 8px;
      border: 1px solid var(--line); background: var(--input); flex-shrink: 0;
      display: block;
    }
    .attach-item a img { cursor: zoom-in; }
    .attach-item .attach-meta { flex: 1; min-width: 0; }
    .attach-item .attach-meta a { color: var(--teal); text-decoration: none; word-break: break-all; }
    .attach-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

    /* Load-more sits under the cards, above the create button. */
    .col-more {
      width: 100%; margin: 6px 0 0; padding: 7px 10px; cursor: pointer;
      border: 1px dashed var(--line); border-radius: 8px;
      background: transparent; color: var(--muted);
      font: inherit; font-size: var(--text-xs);
    }
    .col-more:hover:not(:disabled) { border-style: solid; color: inherit; }
    .col-more:disabled { opacity: .6; cursor: default; }

    /* ===== Task labels ===== */
    .label-field .label-chips {
      display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 8px;
    }
    .label-chip {
      padding: 3px 10px; border-radius: 999px; cursor: pointer;
      font: inherit; font-size: var(--text-xs); font-weight: 600;
      border: 1px solid var(--chip, var(--line));
      background: transparent; color: var(--chip, inherit);
    }
    .label-chip:hover { filter: brightness(1.15); }
    /* Selected reads as filled; unselected as an outline, so the state is
       legible without relying on colour alone. */
    .label-chip.on { background: var(--chip, var(--line)); color: #fff; }
    .label-add { display: flex; gap: 6px; }
    .label-input {
      flex: 1; min-width: 0; padding: 6px var(--space-2);
      font: inherit; font-size: var(--text-xs);
      border: 1px solid var(--line); border-radius: 8px;
      background: var(--input); color: inherit;
    }
    .label-input:focus { outline: none; border-color: var(--teal); }
    .label-add .btn { margin: 0; width: auto; padding: 6px 12px; }

    /* ===== Task activity ===== */
    .activity-section { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }
    .activity-list { display: grid; gap: 8px; margin-top: 10px; }
    .activity-empty { font-size: var(--text-sm); }
    .activity-item { display: flex; align-items: flex-start; gap: 10px; }
    .activity-avatar { margin-top: 1px; }
    .activity-body {
      flex: 1; min-width: 0; display: flex; flex-wrap: wrap;
      align-items: baseline; gap: 6px; font-size: var(--text-sm);
    }
    .activity-time { font-size: var(--text-2xs); }

    /* ===== Task comments ===== */
    .comment-section { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 12px; }
    .comment-list { display: grid; gap: 10px; margin: 10px 0; }
    .comment-empty { font-size: var(--text-sm); padding: var(--space-1) 0; }
    .comment-item {
      display: flex; align-items: flex-start; gap: 10px; padding: 10px;
      border: 1px solid var(--line); border-radius: 10px;
      background: color-mix(in srgb, var(--panel-solid) 80%, transparent);
    }
    .comment-avatar { margin-top: 2px; }
    .comment-body { flex: 1; min-width: 0; }
    .comment-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
    .comment-time { font-size: var(--text-2xs); }
    /* Comment text is user-authored: wrap hard so a long token cannot widen
       the dialog, and keep the author's own line breaks. */
    .comment-text {
      margin-top: var(--space-1); font-size: var(--text-sm);
      line-height: var(--leading-normal);
      overflow-wrap: anywhere; word-break: break-word;
    }
    .comment-composer { display: grid; gap: 8px; }
    .comment-input {
      width: 100%; min-height: 60px; resize: vertical; padding: 8px 10px;
      border: 1px solid var(--line); border-radius: 8px;
      background: var(--input); color: inherit; font: inherit;
    }
    .comment-input:focus { outline: none; border-color: var(--teal); }
    .comment-composer-actions { display: flex; justify-content: flex-end; }
    .comment-composer-actions .btn { margin: 0; width: auto; padding: 6px 14px; }
    .paste-hint { display: none; }
    /* A description is something you read far more often than you edit, so it
       reads as text until you go near it. Boxing it permanently made the
       task's own content look like the eighth field on a form. */
    .desc-composer {
      border: 1px solid transparent;
      border-radius: var(--radius-md);
      background: transparent;
      overflow: hidden;
      transition: border-color var(--dur-fast) var(--ease-out),
                  background var(--dur-fast) var(--ease-out);
    }
    .desc-composer:hover {
      border-color: var(--line);
      background: color-mix(in srgb, var(--text) 3%, transparent);
    }
    .desc-composer:focus-within {
      border-color: var(--accent-blue, var(--teal));
      background: var(--input);
    }
    .desc-composer textarea.desc-sync {
      position: absolute !important;
      width: 1px !important; height: 1px !important;
      opacity: 0; pointer-events: none; overflow: hidden;
      clip: rect(0,0,0,0);
    }
    .desc-editor {
      min-height: 88px;
      max-height: 360px;
      overflow-y: auto;
      padding: 10px 12px;
      outline: none;
      line-height: 1.55;
      white-space: pre-wrap;
      word-break: break-word;
      color: var(--text);
      cursor: text;
    }
    .desc-editor:empty::before {
      content: attr(data-placeholder);
      color: var(--muted);
      pointer-events: none;
    }
    .desc-editor div, .desc-editor p { margin: 0; min-height: 1.35em; }
    .desc-editor hr {
      border: 0;
      border-top: 2px solid color-mix(in srgb, var(--line) 85%, var(--text));
      margin: 12px 0;
      height: 0;
    }
    .desc-editor img {
      max-width: 100%; max-height: 280px; width: auto; height: auto;
      object-fit: contain; border-radius: 6px; border: 1px solid var(--line);
      display: block; background: color-mix(in srgb, var(--panel-solid) 70%, transparent);
      margin: 10px 0 4px;
      user-select: none;
    }
    .desc-preview {
      margin: 0;
      padding: 0 10px 10px;
      display: none;
    }
    .modal-body .issue-field-desc .desc-hint {
      font-size: .75rem; margin-top: 6px; color: var(--muted);
    }
    .side-toggle {
      width: 36px; height: 36px; padding: 0; display: grid; place-items: center;
      font-size: 1.15rem; flex-shrink: 0; border-radius: 8px;
    }
    .shell.side-collapsed .side-toggle#toggleSideBtn::after { content: none; }
    input[type="date"] {
      direction: ltr;
      text-align: start;
      unicode-bidi: isolate;
    }
    .ws-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
    .ws-card { cursor: pointer; border-radius: 16px; padding: 16px; transition: .2s; }
    .ws-card:hover { transform: translateY(-3px); border-color: color-mix(in srgb, var(--teal) 45%, transparent); }
    .modal-bg {
      position: fixed; inset: 0; background: rgba(22,21,15,.5); display: grid; place-items: center;
      padding: 16px; z-index: 30; animation: fadeUp .2s ease;
      backdrop-filter: blur(2px);
    }
    .modal {
      width: min(720px, 100%); border-radius: 14px; padding: 0; animation: pop .25s ease;
      max-height: 90vh; overflow: auto; background: var(--panel-solid); border: 1px solid var(--line);
      box-shadow: var(--elev-3);
    }
    .modal-head {
      display: flex; align-items: center; justify-content: space-between; gap: 12px;
      padding: 10px 14px; border-bottom: 1px solid var(--line);
    }
    .modal-head h2 { margin: 0; font-size: .95rem; font-weight: 600; }
    /* With a breadcrumb above it, the plain "Edit WTM-22" heading is redundant
       to the eye but still names the dialog for a screen reader. */
    .modal-head h2.modal-title-quiet {
      position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip-path: inset(50%); white-space: nowrap;
    }
    .modal-eyebrow {
      display: flex; align-items: center; gap: 8px; min-width: 0;
      font-size: .78rem; color: var(--muted);
    }
    .modal-eyebrow .key {
      display: inline-flex; align-items: center; gap: 5px;
      font-weight: 600; color: var(--muted);
    }
    .modal-eyebrow .key::before {
      content: ""; width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0;
      background: var(--type-color, #4bce97); display: inline-block;
    }
    .modal-eyebrow .key.type-story { --type-color: #4bce97; }
    .modal-eyebrow .key.type-task { --type-color: #579dff; }
    .modal-eyebrow .key.type-bug { --type-color: #f87168; }
    .modal-eyebrow .key.type-subtask { --type-color: #9f8fef; }
    .modal-eyebrow-sep { opacity: .45; }
    .modal-eyebrow-key { color: var(--text); font-weight: 600; }

    .modal-body { padding: 12px 16px 8px; }

    /* --- Split modal (task detail) ---------------------------------------
       Jira-style two-pane issue view: content on the reading side, the
       properties panel on the trailing side. Falls back to one column on
       narrow screens. */
    .modal.modal-split { width: min(940px, 100%); }
    .modal-body.split {
      display: grid;
      grid-template-columns: minmax(0, 1.55fr) minmax(280px, 1fr);
      gap: 0;
      padding: 0;
      align-items: start;
    }
    .modal-pane-main {
      padding: 12px 14px 10px;
      min-width: 0;
      max-height: min(68vh, 620px);
      overflow-y: auto;
    }
    .modal-pane-side {
      padding: 12px 14px 10px;
      min-width: 0;
      border-inline-start: 1px solid var(--line);
      background: color-mix(in srgb, var(--panel-solid) 55%, var(--bg));
      max-height: min(68vh, 620px);
      overflow-y: auto;
      position: sticky; top: 0;
    }
    .attach-section > strong,
    .comment-section > strong,
    .activity-section > strong,
    .modal-pane-side .pane-title {
      display: block;
      font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: .08em;
      color: var(--muted); font-weight: 600; margin: 0 0 10px;
    }
    /* Spacing is set with the details-pane rules further down. */
    .modal-pane-main .issue-field { margin-bottom: 12px; }
    .modal-pane-main .issue-field:first-child label { font-size: .74rem; }
    /* The summary is styled by `.modal-body .issue-field-title input` further
       down. It used to be restated here as well, by id - which wins on
       specificity, so the smaller of the two silently took effect and the
       heading came out narrower than the section labels under it. */
    /* On a phone the two panes stack, and stacked in source order that put
       Status, Assignee and Due date below the description, the attachments,
       every comment and the whole activity log - about two screens down from
       the fields somebody opened the task to change.

       `display: contents` drops the two panes out of the box tree so their
       children become siblings and can be ordered individually: the title,
       then the details, then the long reading material. The panes keep no
       box, so their padding moves to the children that still need it. */
    @media (max-width: 860px) {
      .modal-body.split {
        display: flex; flex-direction: column; padding: 12px 14px 10px;
      }
      .modal-pane-main, .modal-pane-side {
        display: contents; max-height: none; padding: 0;
      }
      .modal-body.split > .modal-pane-main > .issue-field-title { order: 1; }
      .modal-body.split > .modal-pane-side > * { order: 2; }
      .modal-body.split > .modal-pane-side > .pane-title { order: 2; }
      .modal-body.split > .modal-pane-main > .issue-field-desc { order: 3; }
      .modal-body.split > .modal-pane-main > .attach-section { order: 4; }
      .modal-body.split > .modal-pane-main > .comment-section { order: 5; }
      .modal-body.split > .modal-pane-main > .activity-section { order: 6; }
      /* A rule under the details, where the pane's border used to divide. */
      .modal-body.split > .modal-pane-main > .issue-field-desc {
        border-top: 1px solid var(--line); padding-top: 14px; margin-top: 4px;
      }
      /* Sized to their content, the pickers came out as a ragged staircase -
         "To Do" narrow, "Demo User" wide. One column edge instead. */
      .modal-body.split .issue-field select,
      .modal-body.split .issue-field input { width: 100%; max-width: none; }
    }
    .modal-body label {
      display: flex; align-items: center; gap: 10px; margin: 0 0 6px;
      font-size: .82rem; color: var(--muted); font-weight: 600;
    }
    .modal-body .issue-field { margin-bottom: 12px; }
    .modal-body .issue-field-check { margin-bottom: 10px; }

    /* These follow `.modal-body label` deliberately: it matches at the same
       specificity, so anything defined before it silently loses. */

    /* The summary is the dialog's real heading, so it is sized like one and
       loses the box that made it look like the seventh field on the form. */
    .modal-body .issue-field-title label { display: none; }
    .modal-body .issue-field-title input {
      font-size: var(--text-xl); font-weight: 500; line-height: 1.3;
      letter-spacing: -.015em;
      padding: 6px 8px; margin: 0 0 4px;
      border-color: transparent; background: transparent;
    }
    .modal-body .issue-field-title input:hover {
      background: color-mix(in srgb, var(--text) 6%, transparent);
    }
    .modal-body .issue-field-title input:focus {
      background: var(--input); border-color: var(--accent-blue, #5b57d8);
    }

    /* "Assign to me" and friends: a field's own shortcut, on its own line. */
    .modal-body .issue-field label.with-action {
      display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    }
    .modal-body .field-action {
      border: 0; background: transparent; padding: 0; margin: 0; cursor: pointer;
      font: inherit; font-size: .76rem; font-weight: 600;
      color: var(--accent-blue, #5b57d8); text-transform: none; letter-spacing: 0;
    }
    .modal-body .field-action:hover { text-decoration: underline; }

    /* Issue key and avatar are buttons now, but must still read as plain card
       furniture until you hover them. */
    .card .key-link {
      background: transparent; border: 0; padding: 0; margin: 0;
      /* Match the plain .key text it replaced — `font: inherit` alone would
         pull in the card's larger body size. */
      font-family: inherit; font-size: .72rem; font-weight: 600;
      letter-spacing: .02em; line-height: inherit;
      color: var(--muted); cursor: pointer;
      border-radius: 4px; text-decoration: none;
    }
    .card .key-link:hover { color: var(--teal); text-decoration: underline; }
    .card .avatar-assign {
      border: 0; padding: 0; cursor: pointer; font: inherit;
      transition: transform .15s ease, box-shadow .15s ease;
    }
    .card .avatar-assign:hover {
      transform: scale(1.12);
      box-shadow: 0 0 0 2px color-mix(in srgb, var(--teal) 55%, transparent);
    }

    /* --- In-app confirmation ---------------------------------------------- */
    .confirm-bg {
      position: fixed; inset: 0; z-index: 140;
      display: grid; place-items: center; padding: 20px;
      background: rgba(9, 12, 20, .55); backdrop-filter: blur(2px);
    }
    .confirm-card {
      width: min(420px, 100%);
      background: var(--panel-solid); border: 1px solid var(--line);
      border-radius: 14px; padding: 22px;
      box-shadow: 0 28px 60px -24px rgba(0,0,0,.6);
      display: grid; grid-template-columns: auto 1fr; gap: 14px 14px;
      animation: pop .16s ease;
    }
    .confirm-icon {
      width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
      font-size: 1.15rem; font-weight: 600; flex-shrink: 0;
      background: color-mix(in srgb, var(--teal) 18%, transparent); color: var(--teal);
    }
    .confirm-icon.danger {
      background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger);
    }
    .confirm-text h3 { margin: 4px 0 0; font-size: 1.02rem; font-weight: 600; line-height: 1.35; }
    .confirm-text p { margin: 8px 0 0; color: var(--muted); font-size: .9rem; line-height: 1.55; }
    .confirm-actions {
      grid-column: 1 / -1;
      display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap;
    }
    .confirm-actions .btn { width: auto; margin: 0; padding: 9px 18px; }

    /* --- Right-click menu -------------------------------------------------- */
    .ctx-menu {
      position: fixed; z-index: 120; min-width: 190px;
      background: var(--panel-solid); border: 1px solid var(--line);
      border-radius: 10px; padding: 5px;
      box-shadow: 0 18px 44px -18px rgba(0,0,0,.55), 0 0 0 1px rgba(0,0,0,.04);
      animation: pop .12s ease;
    }
    .ctx-item {
      display: flex; align-items: center; gap: 10px; width: 100%;
      background: transparent; border: 0; border-radius: 7px;
      padding: 8px 10px; margin: 0; cursor: pointer;
      font: inherit; font-size: .87rem; font-weight: 600; color: var(--text);
      text-align: start;
    }
    .ctx-item:hover:not(:disabled) { background: color-mix(in srgb, var(--teal) 16%, transparent); }
    .ctx-item:disabled { opacity: .45; cursor: default; }
    .ctx-item.danger { color: var(--danger); }
    .ctx-item.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 15%, transparent); }
    .ctx-icon { width: 16px; text-align: center; flex-shrink: 0; opacity: .85; }
    .ctx-sep { height: 1px; background: var(--line); margin: 5px 4px; }
    .notif-item.notif-linked { cursor: pointer; }
    .notif-item.notif-linked:hover {
      background: color-mix(in srgb, var(--teal) 12%, transparent);
    }
    .ctx-note {
      font-size: .88rem; line-height: 1.55; color: var(--muted);
      background: color-mix(in srgb, var(--danger) 9%, transparent);
      border: 1px solid color-mix(in srgb, var(--danger) 25%, transparent);
      border-radius: 8px; padding: 10px 12px;
    }

    /* --- Compact form (event editor) -------------------------------------
       Two columns, tight rows, no dialog scrollbar on a normal screen. */
    .modal.modal-compact { width: min(660px, 100%); max-height: 94vh; }
    .modal-body.compact {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 6px 14px;
      padding: 8px 16px 6px;
      align-content: start;
    }
    .modal-body.compact .issue-field,
    .modal-body.compact .issue-field-check {
      grid-column: span 2;      /* full width unless the field asks for half */
      margin-bottom: 0;
      min-width: 0;
    }
    .modal-body.compact .issue-field.half,
    .modal-body.compact .issue-field-check.half { grid-column: span 1; }
    .modal-body.compact .issue-field.hidden { display: none; }
    .modal-body.compact label {
      font-size: .72rem; margin: 0 0 3px; letter-spacing: .01em;
    }
    .modal-body.compact input,
    .modal-body.compact select { padding: 6px 9px; }
    .modal-body.compact textarea { min-height: 32px; padding: 5px 9px; }
    .modal-body.compact .check-line { font-size: .84rem; }
    /* The attendee list is a multi-select; keep it three rows tall so it does
       not stretch the dialog past the viewport. */
    .modal-body.compact select[multiple] { height: 46px; padding: 2px 6px; }
    .modal-body.compact select[multiple] option { padding: 2px 4px; }
    .modal-body.compact .muted { font-size: .7rem !important; margin-top: 3px !important; }
    .modal-body.compact .issue-field-check {
      align-self: end; padding-bottom: 6px;
    }
    /* Colour sits beside the attendee list, so keep the swatches on one line. */
    .modal-body.compact .color-row { flex-wrap: wrap; align-items: center; }
    .modal-body.compact .color-preset { width: 16px; height: 16px; }
    .modal-body.compact input[type="color"] { width: 34px; height: 28px; padding: 2px; }
    .modal-compact .modal-head { padding: 9px 16px; }
    .modal-compact .modal-foot { padding: 9px 16px; }
    .modal-compact .modal-head h2 { font-size: .95rem; }
    /* The title reads as the dialog heading, like Teamup's big title box. */
    .modal-body.compact #mf_title { font-size: .94rem; font-weight: 600; padding: 7px 10px; }
    .modal-body.compact .color-row { gap: 6px; }
    .modal-body.compact .color-presets { gap: 4px; }
    @media (max-width: 560px) {
      .modal-body.compact { grid-template-columns: 1fr; }
      .modal-body.compact .issue-field.half,
      .modal-body.compact .issue-field-check.half { grid-column: span 1; }
    }
    .modal-body .check-line {
      display: flex; align-items: center; gap: 9px; margin: 0;
      font-size: .9rem; font-weight: 600; color: var(--text); cursor: pointer;
    }
    .modal-body .check-line input[type="checkbox"] {
      width: 16px; height: 16px; margin: 0; padding: 0; accent-color: var(--teal);
      flex: 0 0 auto; cursor: pointer;
    }
    /* From/To and the two clock fields read better side by side. */
    .modal-body .field-row {
      display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
    }
    @media (max-width: 520px) { .modal-body .field-row { grid-template-columns: 1fr; } }
    .modal-body input, .modal-body select, .modal-body textarea {
      border-radius: 3px; padding: 8px 10px;
    }
    .modal-body textarea { min-height: 90px; }
    .modal-foot {
      display: flex; gap: 8px; flex-wrap: wrap; align-items: center; justify-content: flex-end;
      padding: 10px 14px; border-top: 1px solid var(--line); position: sticky; bottom: 0;
      background: var(--panel-solid);
    }
    .modal h2 { margin: 0 0 8px; }
    .toast {
      position: fixed; bottom: 18px; inset-inline-end: 18px; background: var(--teal-dim); color: #ecfdf5;
      padding: 12px 14px; border-radius: 12px; z-index: 40; display: none; max-width: min(420px, 90vw);
      box-shadow: var(--shadow); animation: fadeUp .25s ease;
    }
    .bell-wrap { position: relative; }
    .badge {
      position: absolute; top: -4px; inset-inline-end: -4px; background: #e11d48; color: white;
      border-radius: 999px; font-size: .65rem; min-width: 16px; height: 16px;
      display: grid; place-items: center; padding: 0 4px; animation: pulse 1.4s infinite;
    }
    .notif-panel {
      position: absolute; inset-inline-end: 0; top: 42px; width: min(340px, 90vw);
      background: var(--panel-solid); border: 1px solid var(--line); border-radius: 14px;
      box-shadow: var(--shadow); padding: 10px; z-index: 20; max-height: 360px; overflow: auto;
    }
    .notif-item { padding: 10px; border-radius: 10px; border: 1px solid transparent; margin-bottom: 6px; cursor: pointer; }
    .notif-item.unread { background: color-mix(in srgb, var(--teal) 10%, transparent); border-color: var(--line); }
    .role-grid { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); margin-top: 10px; }
    .role-card { border: 1px solid var(--line); border-radius: 12px; padding: 12px; background: var(--input); }
    .role-card strong { display: block; margin-bottom: 4px; color: var(--teal); }
    .invite-box { display: grid; gap: 10px; grid-template-columns: 1fr 140px auto; align-items: end; }
    .stats-grid {
      display: grid; gap: 12px;
      grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    }
    /* Six numbers across the top of the dashboard. They are a summary, not
       the point of the screen, so they stopped being big soft boxes that
       lift when the mouse goes near them. */
    .stat-card {
      border: 1px solid var(--line); border-radius: var(--radius-lg);
      padding: var(--space-3) var(--space-4);
      background: var(--panel-solid);
    }

    /* Two lists side by side on a desktop, stacked below it. */
    .dash-grid {
      display: grid; gap: 12px; margin-top: 14px;
      grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
      align-items: start;
    }
    .dash-panel {
      border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
      background: color-mix(in srgb, var(--panel-solid) 85%, transparent);
    }
    .dash-panel-head {
      display: flex; align-items: center; justify-content: space-between; gap: 8px;
      padding: 12px 14px; border-bottom: 1px solid var(--line);
    }
    .dash-panel-head h3 {
      margin: 0; font-size: .84rem; font-weight: 600; letter-spacing: .03em;
      text-transform: uppercase; color: var(--muted);
    }
    .dash-panel-more { margin: 0; padding: 5px 10px; font-size: .78rem; }
    .dash-list { display: flex; flex-direction: column; }
    .dash-list .skel { margin: 10px 14px; }
    .dash-empty { margin: 0; padding: 18px 14px; font-size: .88rem; }
    /* Rows are buttons: a list you can only click with a mouse is a list half
       the users cannot reach. */
    .dash-row {
      display: flex; align-items: center; gap: 10px; width: 100%; margin: 0;
      padding: 10px 14px; border: 0; border-top: 1px solid var(--line);
      background: transparent; color: var(--text); cursor: pointer;
      text-align: start; font: inherit; min-width: 0;
    }
    .dash-row:first-child { border-top: 0; }
    .dash-row:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
    .dash-row .key {
      color: var(--muted); font-size: .72rem; font-weight: 600; flex: 0 0 auto;
      display: inline-flex; align-items: center; gap: 5px;
    }
    .dash-row .key::before {
      content: ""; width: 12px; height: 12px; border-radius: 2px; flex-shrink: 0;
      background: var(--type-color, #4bce97); display: inline-block;
    }
    .dash-row .key.type-story { --type-color: #4bce97; }
    .dash-row .key.type-task { --type-color: #579dff; }
    .dash-row .key.type-bug { --type-color: #f87168; }
    .dash-row .key.type-subtask { --type-color: #9f8fef; }
    .dash-row-title {
      flex: 1 1 auto; min-width: 0; font-size: .88rem; font-weight: 500;
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .dash-due {
      flex: 0 0 auto; font-size: var(--text-2xs); font-weight: 500; color: var(--muted);
      padding: 2px 8px; border-radius: var(--radius-pill);
      background: color-mix(in srgb, var(--text) 7%, transparent);
    }
    .dash-due.is-overdue {
      color: var(--tone-hot-ink); background: var(--tone-hot-bg);
    }
    .stat-card .stat-label {
      color: var(--muted); font-size: var(--text-xs); margin-bottom: var(--space-2);
    }
    /* The number is the content; it does not need to be the accent colour as
       well as the biggest thing in the box. */
    .stat-card .stat-value {
      font-size: var(--text-2xl); font-weight: 500; color: var(--text);
      line-height: 1; letter-spacing: -.02em;
    }
    @media (max-width: 720px) {
      .invite-box { grid-template-columns: 1fr; }
      .top { gap: 8px; }
    }
    .help-banner {
      border: 1px solid var(--line);
      background: color-mix(in srgb, var(--accent-blue, #5b57d8) 10%, transparent);
      border-radius: 6px; padding: 10px 12px; margin-bottom: 12px; color: var(--muted); font-size: .85rem; line-height: 1.45;
    }
    #authView .lang-toggle {
      border: 1px solid #E2E8F0; border-radius: 8px; background: #fff;
    }
    #authView .lang-toggle button {
      background: transparent; color: #64748B; border: 0;
      padding: 6px 10px; font-weight: 600; font-size: .75rem; cursor: pointer;
    }
    .auth-legal {
      margin-top: 18px; text-align: center; font-size: .75rem; color: #64748B; line-height: 1.5;
    }
    .auth-legal a { color: #5b57d8; text-decoration: none; font-weight: 600; }
    .auth-back-home {
      background: none; border: 0; color: #5b57d8; cursor: pointer;
      font-weight: 600; padding: 0; margin: 0 0 12px; font-size: .85rem;
      display: block; width: 100%; text-align: start;
    }
    #authView.auth-wrap,
    #wsView.auth-wrap { background: #fff !important; }
    #authView .auth-toolbar .theme-select { display: none; }
    #authView .auth-card {
      border: 1px solid #E2E8F0;
      box-shadow: 0 12px 40px rgba(15,23,42,.06);
      background: #fff; color: #0F172A; width: min(420px, 100%);
      border-radius: 16px;
    }
    #authView .atl-logo .word { color: #0F172A !important; }
    #authView .atl-title { font-weight: 600; font-size: 1.15rem; color: #0F172A; }
    #authView .auth-card label { color: #334155; }
    #authView .auth-card input[type="email"],
    #authView .auth-card input[type="password"],
    #authView .auth-card input[type="text"],
    #authView .auth-card input:not([type]) {
      background: #F8FAFC !important; color: #0F172A !important;
      border: 1px solid #E2E8F0 !important; border-radius: 8px;
    }
    #authView .auth-card input:focus {
      border-color: #5b57d8 !important; box-shadow: 0 0 0 3px rgba(91,87,216,.15); outline: none;
    }
    #authView .auth-email-chip { background: #EEF2FF !important; color: #3f3ba8 !important; }
    #authView .btn.atl-primary {
      background: #5b57d8; border-radius: 8px;
      box-shadow: 0 6px 16px rgba(91,87,216,.25);
    }
    #authView .btn.atl-primary:hover { background: #3f3ba8; }
    #authView .auth-footer-links button.linkish,
    #authView .auth-step-back { color: #5b57d8; }
    #authView .sso-btn {
      border-radius: 8px; background: #fff; color: #0F172A; border: 1px solid #E2E8F0;
    }
    #authView .sso-btn:hover { background: #F8FAFC; border-color: #C7D2FE; }
    #authView .oauth-divider { color: #64748B; }
    #authView .muted, #authView .auth-subfoot { color: #64748B; }
    #authView .help-banner { background: #EEF2FF; border-color: #C7D2FE; color: #312E81; }
    #authView .error { color: #DC2626; }
    #authView .bot-check { color: #334155; }

    /* Workspace picker — always light surface (readable even if app theme is Dark) */
    #wsView .auth-card {
      border: 1px solid #E2E8F0;
      box-shadow: 0 12px 40px rgba(15,23,42,.06);
      background: #fff;
      color: #0F172A;
      border-radius: 16px;
    }
    #wsView .brand { color: #0F172A; }
    #wsView .brand span { color: #5b57d8; }
    #wsView .muted { color: #64748B !important; }
    #wsView .theme-select {
      background: #F8FAFC;
      color: #0F172A;
      border: 1px solid #CBD5E1;
      border-radius: 8px;
      font-weight: 600;
    }
    #wsView .theme-select option { background: #fff; color: #0F172A; }
    #wsView .btn.ghost {
      color: #5b57d8 !important;
      border: 1px solid #E2E8F0;
      background: #fff;
      border-radius: 8px;
      font-weight: 600;
    }
    #wsView .btn.ghost:hover { background: #EEF2FF; border-color: #C7D2FE; }
    #wsView .lang-toggle {
      border: 1px solid #E2E8F0;
      border-radius: 8px;
      overflow: hidden;
    }
    #wsView .lang-toggle button { color: #64748B; background: #fff; }
    #wsView .lang-toggle button.active {
      background: #EEF2FF;
      color: #3f3ba8;
    }
    #wsView .ws-card.panel,
    #wsView .ws-card {
      background: #F8FAFC !important;
      border: 1px solid #E2E8F0 !important;
      color: #0F172A !important;
      box-shadow: none !important;
    }
    #wsView .ws-card:hover {
      border-color: #A5B4FC !important;
      background: #EEF2FF !important;
      transform: translateY(-2px);
    }
    #wsView .ws-card .muted { color: #64748B !important; }
    #wsView #createWsBtn {
      background: #5b57d8;
      border-radius: 8px;
      box-shadow: 0 6px 16px rgba(91,87,216,.25);
      margin-top: 16px;
    }
    #wsView #createWsBtn:hover { background: #3f3ba8; filter: none; }

    /* ===== Subscription notice =====
       Above the tabs so it is visible on every screen, and colour-coded by
       urgency - but the wording carries the meaning, not the colour. */
    .billing-banner {
      padding: 10px var(--space-4); font-size: var(--text-sm); font-weight: 600;
      border-bottom: 1px solid var(--line);
      display: flex; align-items: center; gap: 8px;
    }
    .billing-banner.info {
      background: color-mix(in srgb, #2563EB 12%, transparent);
      color: inherit;
    }
    .billing-banner.warn {
      background: color-mix(in srgb, #F59E0B 20%, transparent);
      color: inherit;
    }
    .billing-banner.danger {
      background: color-mix(in srgb, #DC2626 16%, transparent);
      color: inherit;
    }
    .billing-banner::before {
      content: "!"; flex: 0 0 auto;
      width: 18px; height: 18px; border-radius: 999px;
      display: grid; place-items: center;
      font-size: .7rem; font-weight: 600; color: #fff;
      background: currentColor; opacity: .75;
    }
    .billing-banner.info::before { background: #2563EB; opacity: 1; }
    .billing-banner.warn::before { background: #B45309; opacity: 1; }
    .billing-banner.danger::before { background: #DC2626; opacity: 1; }

    /* ===== Loading skeletons =====
       A "Loading..." string tells you nothing about what is coming. These
       mirror the shape of the board so the layout does not jump when the real
       cards land. */
    @keyframes wtm-shimmer {
      from { background-position: 200% 0; }
      to   { background-position: -200% 0; }
    }
    .skel {
      background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--line) 45%, transparent) 25%,
        color-mix(in srgb, var(--line) 80%, transparent) 37%,
        color-mix(in srgb, var(--line) 45%, transparent) 63%
      );
      background-size: 200% 100%;
      animation: wtm-shimmer 1.4s ease-in-out infinite;
      border-radius: 6px;
    }
    .skel-line { height: 10px; margin-bottom: 8px; }
    .skel-line.short { width: 45%; }
    .skel-line.medium { width: 70%; }
    .skel-card {
      padding: 12px; border: 1px solid var(--line); border-radius: 10px;
      background: var(--panel-solid); margin-bottom: 10px;
    }
    .skel-card .skel-line:last-child { margin-bottom: 0; }
    .skel-col {
      flex: 0 0 auto; width: 280px; padding: 10px;
      border: 1px solid var(--line); border-radius: 12px;
      background: color-mix(in srgb, var(--panel-solid) 60%, transparent);
    }
    .skel-col-head {
      display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
    }
    .skel-col-head .skel { height: 12px; width: 90px; }

    /* ===== Empty states =====
       A bare "No projects" line reads like a failure. Say what is missing and
       what to do about it. */
    .empty-state {
      display: grid; justify-items: center; align-content: start; gap: 6px;
      padding: 40px 20px; text-align: center;
      /* Inside a stretching flex parent (the board is one) the grid rows would
         otherwise spread out across the full height. */
      flex: 1; width: 100%; align-self: start;
    }
    .empty-state .empty-icon {
      font-size: 2rem; line-height: 1; opacity: .55; margin-bottom: 2px;
    }
    .empty-state .empty-title { font-weight: 600; font-size: var(--text-base); }
    .empty-state .empty-hint {
      color: var(--muted); font-size: var(--text-sm); max-width: 42ch;
      line-height: var(--leading-normal);
    }
    .empty-state .btn { width: auto; margin-top: 8px; }

    /* ===== Motion =====
       Short and small: enough to show where a thing came from, never enough to
       wait for. */
    @keyframes wtm-rise {
      from { opacity: 0; transform: translateY(6px); }
      to   { opacity: 1; transform: none; }
    }
    @keyframes wtm-pop {
      from { opacity: 0; transform: scale(.97); }
      to   { opacity: 1; transform: none; }
    }
    .kanban .card { animation: wtm-rise var(--dur-base) var(--ease-out) both; }
    .modal { animation: wtm-pop var(--dur-fast) var(--ease-out) both; }
    .modal-bg { animation: wtm-rise var(--dur-fast) var(--ease-out) both; }
    .empty-state { animation: wtm-rise var(--dur-base) var(--ease-out) both; }
    .toast { transition: opacity .2s ease, transform .2s ease; }

    /* Hover lift on things you can act on, kept off touch devices where there
       is no hover and the transform only causes repaint churn. */
    @media (hover: hover) {
      .kanban .card {
        transition: transform var(--dur-fast) var(--ease-out),
                    box-shadow var(--dur-fast) var(--ease-out);
      }
      .kanban .card:hover {
        transform: translateY(-1px);
        box-shadow: var(--elev-2);
      }
    }

    /* Someone who asked the OS for less motion means it, and vestibular
       disorders make this a health matter rather than a taste one.
       The app animates twenty-odd selectors and only the marketing page was
       guarded, so this covers everything at once - including anything added
       later. Durations collapse rather than going to `none` so animation and
       transition end events still fire and nothing waits forever on them. */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
      }
      /* The shimmer is decorative; hold it still rather than flashing once. */
      .skel { animation: none !important; background-position: 0 0; }
      .kanban .card:hover { transform: none; }
    }

    /* ===== Keyboard accessibility =====
       The stylesheet clears the browser's outline in seven places, which left
       keyboard users with no idea where they were. :focus-visible restores a
       ring for keyboard and assistive-tech focus only, so a mouse click still
       does not draw one. Declared last so it wins over those `outline: none`
       rules on equal specificity. */
    :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
      outline: 2px solid var(--focus-ring, #2563EB);
      outline-offset: 2px;
      border-radius: 4px;
    }
    /* Ring needs to stay visible against the accent fills it lands on. */
    .btn.create:focus-visible,
    .label-chip.on:focus-visible {
      outline-color: #fff;
      outline-offset: -4px;
    }
    :root { --focus-ring: #2563EB; }
    :root[data-theme="dark"], [data-theme="dark"] { --focus-ring: #7aa7ff; }

    /* Visible only once focused, so the keyboard path to the content is one
       key press instead of tabbing through the whole nav. */
    .skip-link {
      position: absolute; inset-inline-start: 8px; top: -60px; z-index: 100;
      padding: 10px 16px; border-radius: 0 0 8px 8px;
      background: var(--panel-solid, #fff); color: inherit;
      border: 1px solid var(--line); font-weight: 600; text-decoration: none;
      transition: top .15s ease;
    }
    .skip-link:focus { top: 0; }

    @media (prefers-reduced-motion: reduce) {
      .skip-link { transition: none; }
    }

    /* ===== Small screens =====
       The sidebar used to reflow into a wrapping row above the content, which
       pushed the board 810px down a 375px-tall phone screen - a whole viewport
       of chrome before the first card. It is an off-canvas drawer instead, so
       the content starts at the top and the nav slides over it on demand. */
    @media (max-width: 860px) {
      .shell, html[dir="rtl"] .shell,
      .shell.side-collapsed, html[dir="rtl"] .shell.side-collapsed {
        grid-template-columns: minmax(0, 1fr);
      }
      .app-main, html[dir="rtl"] .app-main {
        grid-column: 1; grid-row: 1; max-height: none;
      }
      .app-side, html[dir="rtl"] .app-side {
        position: fixed; top: 0; bottom: 0; left: 0; right: auto;
        width: min(272px, 82vw); max-height: none; z-index: 30;
        grid-column: auto; grid-row: auto;
        flex-direction: column; flex-wrap: nowrap;
        padding: 12px 10px; border: 0;
        border-inline-end: 1px solid var(--line);
        box-shadow: 0 0 40px rgba(0, 0, 0, .28);
        transform: translateX(-100%);
        transition: transform .22s ease;
        /* The desktop collapsed state fades and disables the panel; on mobile
           the drawer's own open state governs, so undo that here. */
        opacity: 1; pointer-events: auto; overflow-y: auto;
      }
      html[dir="rtl"] .app-side {
        left: auto; right: 0;
        border-inline-end: 0; border-inline-start: 1px solid var(--line);
        transform: translateX(100%);
      }
      .shell.side-collapsed .app-side { display: flex; }
      .shell.side-open .app-side,
      html[dir="rtl"] .shell.side-open .app-side { transform: translateX(0); }

      .side-backdrop {
        position: fixed; inset: 0; z-index: 29; border: 0; padding: 0;
        background: rgba(0, 0, 0, .45);
        opacity: 0; pointer-events: none; transition: opacity .22s ease;
      }
      .shell.side-open .side-backdrop { opacity: 1; pointer-events: auto; }

      /* Collapsing is a desktop affordance; on a drawer it would only hide the
         close control. */
      #toggleSideBtnSide { display: none; }

      /* Header controls and tabs each wrapped onto three rows. One scrolling
         row keeps every control reachable without eating the screen. */
      .top { flex-wrap: nowrap; gap: 8px; padding: 8px 10px; }
      .top-search { flex: 1 1 auto; min-width: 0; max-width: none; }
      .top-actions {
        flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
        -webkit-overflow-scrolling: touch; max-width: 58vw;
      }
      .top-actions::-webkit-scrollbar { display: none; }
      .top-actions > * { flex: 0 0 auto; }
      .project-tabs {
        flex-wrap: nowrap; padding: 0 10px; scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
      }
      .project-tabs::-webkit-scrollbar { display: none; }

      /* Panel toolbars carry a title plus four or five controls, which wrapped
         onto three rows and pushed the board down again. Title on its own line,
         controls in one scrolling row underneath. */
      .content { padding: 12px 10px; }
      .row { gap: 8px; margin-bottom: 8px; align-items: flex-start; }
      .row-actions {
        flex-wrap: nowrap; overflow-x: auto; max-width: 100%;
        scrollbar-width: none; -webkit-overflow-scrolling: touch;
      }
      .row-actions::-webkit-scrollbar { display: none; }
      .row-actions > * { flex: 0 0 auto; }
      .board-bar { flex-wrap: nowrap; gap: 8px; align-items: center; }
      /* The picker keeps a fixed slice; the tools take the rest and scroll
         inside it. Without min-width:0 the tools row refuses to shrink and
         lands on top of the picker instead of scrolling. */
      .board-bar-project { flex: 0 0 auto; min-width: 0; }
      .board-bar-project select { min-width: 0; width: 148px; }
      .board-bar-tools {
        flex: 1 1 0; min-width: 0;
        flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
      }
      .board-bar-tools::-webkit-scrollbar { display: none; }
      .board-bar-tools > * { flex: 0 0 auto; }

      /* A column that fits the screen with a sliver of the next one showing,
         so it reads as a scrollable board rather than a single list. */
      .kanban .col { min-width: 82vw; max-width: 82vw; }

      /* 14px is what a dense desktop tool runs at. On a phone held at arm's
         length the smallest step off that ramp lands under 10px, which is
         not small, it is unreadable. */
      html { font-size: 15px; }

      /* Safari zooms the whole page in when you focus a field smaller than
         16px, and it does not zoom back out - which is most of what "it
         looks broken on my phone" turns out to mean. The 16px is not a
         design choice; it is the threshold. */
      input, select, textarea,
      .top-search input { font-size: 16px; }

      /* Anything you tap with a thumb. Below about 40px the miss rate on a
         moving bus is what people remember about the product. */
      .nav button, .top-actions .btn, .row-actions .btn,
      .board-bar-tools .btn, .btn.create { min-height: 40px; }
      .side-toggle { width: 40px; height: 40px; }

      /* A scroll that reaches its end should stop, not drag the page behind
         it - and on iOS, not bounce the whole document either. */
      .col-cards, .kanban, .top-actions, .row-actions,
      .board-bar-tools, .project-tabs { overscroll-behavior: contain; }
    }

    /* ===== The top bar on a phone =====
       The search box took 157px of a 390px screen, so the five actions beside
       it needed 271px and got 156. The bell and your own avatar sat off the
       right edge - and the avatar is where sign-out, theme and language live.
       Reachable only by scrolling a strip you cannot see is not reachable.

       So the search collapses to its own icon and opens when tapped. It is
       the one control here that can afford to: everything else is already an
       icon, and the field is no use until you are typing in it anyway. */
    @media (max-width: 560px) {
      .top-search {
        flex: 0 0 auto; width: 42px; padding: 7px 10px; gap: 0;
        cursor: text; overflow: hidden;
        transition: width var(--dur-base) var(--ease-out),
                    padding var(--dur-base) var(--ease-out);
      }
      .top-search input { min-width: 0; }
      .top-search.is-open {
        flex: 1 1 auto; width: auto; min-width: 0; padding: 7px 14px; gap: 8px;
        overflow: visible;   /* or the results are clipped by the pill */
      }
      /* Anchored to the header instead of the pill: at this width the pill is
         narrower than the answer, and half a result is no result. */
      .search-panel {
        position: fixed; inset-inline: 10px; top: 62px; width: auto;
        max-height: 62vh;
      }
      .top-actions { max-width: none; overflow: visible; }
      .top-search.is-open ~ .top-actions {
        overflow-x: auto; scrollbar-width: none;
      }
      /* One row of the tab strip is enough; the drawer lists them all. */
      .project-tabs { font-size: var(--text-sm); }
    }

    /* ===== Notches and home indicators =====
       The page paints edge to edge (viewport-fit=cover in index.html), so the
       insets have to be given back deliberately. Without these the top bar
       hides under the status bar and the last row of any list sits beneath
       the home indicator, where a tap swipes the app away instead. */
    @supports (padding: env(safe-area-inset-top)) {
      @media (max-width: 860px) {
        .top {
          padding-top: calc(8px + env(safe-area-inset-top));
          padding-inline: calc(10px + env(safe-area-inset-left))
                          calc(10px + env(safe-area-inset-right));
        }
        .content {
          padding-bottom: calc(16px + env(safe-area-inset-bottom));
          padding-inline: calc(10px + env(safe-area-inset-left))
                          calc(10px + env(safe-area-inset-right));
        }
        .app-side { padding-top: calc(12px + env(safe-area-inset-top)); }
        .modal-bg { padding-bottom: calc(16px + env(safe-area-inset-bottom)); }
        .kanban { height: calc(var(--app-h) - 196px - env(safe-area-inset-bottom)); }
      }
    }

    /* Above the breakpoint the sidebar is part of the grid, not a drawer. */
    @media (min-width: 861px) {
      .side-backdrop { display: none; }
      /* The sidebar already lists every destination. Repeating all seven as a
         tab strip underneath it gave the same navigation twice on one screen -
         and cost a whole row of vertical space above the board. The strip is
         the drawer breakpoint's only navigation, so it stays there. */
      .project-tabs { display: none; }
    }
