  :root{
    --ink:#1F3A34; --ink-soft:#3E5850;
    --paper:#F5F1E8; --paper-raised:#FFFFFF;
    --line:#DAD3C2; --line-strong:#C3BAA4;
    --mustard:#B8792E; --mustard-soft:#EFDFC7;
    --sage:#7C9082; --sage-soft:#E4E9DF;
    --brick:#B8433C; --brick-soft:#F3DEDB;
    --blue:#3E6E8E; --blue-soft:#DDE8EE;
    --green-dot:#4C8B5C; --yellow-dot:#D4A62B;
    --text-muted:#8A8570; --subtle-bg:#FBF9F3; --radius:6px;
    /*
     * Typography. A font set swaps display and ui; --font-mono never changes,
     * because it is doing alignment work in counts, SKUs and money columns
     * rather than decoration, and a proportional face there would break the
     * columns. Sets live in the html[data-font=…] blocks further down.
     */
    --font-display:'Fraunces', serif;
    --font-ui:'IBM Plex Sans', sans-serif;
    --font-mono:'IBM Plex Mono', monospace;
    /*
     * Block style — how content panels are drawn. Every content panel reads
     * these, so a style is six declarations rather than a hunt through the
     * stylesheet. Chrome that isn't content (modals, dropdowns, the floating
     * chat panel) deliberately doesn't use them: a dropdown with no border
     * because somebody chose Flat would be unreadable over the page behind it.
     */
    --panel-bg:var(--paper-raised);
    --panel-border:1px solid var(--line);
    --panel-radius:8px;
    --panel-pad:16px 18px;
    --panel-shadow:none;
    --row-pad:13px 16px;
    /* Nav category tints. Every group head is a filled band so it can't be
       mistaken for one of the tabs underneath it. Each family needs a soft
       fill AND a matching dark ink — text on a tint must come from the same
       hue, or it reads as a mistake. Plum and teal are new: the palette had
       four usable families and there are seven groups. Apps is violet rather
       than the obvious green because green sits on top of CRM's sage — two
       adjacent groups reading as the same colour defeats the point. */
    --violet:#6A6AA8; --violet-soft:#E3E2F2;
    --plum:#8A5A7A; --plum-soft:#EDE0E8;
    --teal:#3E8E85; --teal-soft:#DAEBE8;
    --sage-ink:#3A5240; --mustard-ink:#7A4E17; --blue-ink:#2A4C63;
    --violet-ink:#3F3F73; --brick-ink:#8A2F29; --plum-ink:#6B4159;
    --teal-ink:#2C6660;
  }
  /*
   * Themes. `:root` above is Garden paper, the default, so a theme block only
   * has to restate what differs — except that in practice every block restates
   * ALL of them. A theme that overrode only paper and ink would inherit Garden
   * paper's nav tints, and the sidebar headers would come out as pale mint on a
   * dark background. Copy a whole block when adding one.
   *
   * `data-theme` names the specific theme; `data-scheme` says light or dark and
   * is what the handful of scheme-specific rules below key on, so adding a
   * fourth dark theme doesn't mean editing ten selectors. applyTheme() sets
   * both.
   */

  /*
   * Block styles. Framed is :root, so it needs no block.
   *
   * Elevated is the only one that has to know about the colour scheme: a shadow
   * tuned for cream paper disappears on a near-black one. That is what
   * data-scheme is for, and why it exists separately from data-theme — one rule
   * covers all five dark themes.
   */
  html[data-blocks="flat"]{
    --panel-bg:var(--subtle-bg);
    --panel-border:1px solid transparent;
    --panel-shadow:none;
  }
  html[data-blocks="elevated"]{
    --panel-border:1px solid transparent;
    --panel-radius:12px;
    --panel-shadow:0 2px 8px rgba(31,58,52,0.10), 0 1px 2px rgba(31,58,52,0.06);
  }
  html[data-scheme="dark"][data-blocks="elevated"]{
    --panel-shadow:0 2px 10px rgba(0,0,0,0.45), 0 1px 3px rgba(0,0,0,0.35);
  }
  /* No panels at all. The section heading and its rule do the separating, the
     way a printed report does it. Rows keep their own dividers, or a list would
     become an undifferentiated block of text. */
  html[data-blocks="ruled"]{
    --panel-bg:transparent;
    --panel-border:none;
    --panel-radius:0;
    --panel-pad:0;
    --panel-shadow:none;
  }
  html[data-blocks="ruled"] .section-head{border-bottom:1px solid var(--line);padding-bottom:8px;}
  html[data-blocks="ruled"] .row{padding-left:0;padding-right:0;}
  html[data-blocks="compact"]{
    --panel-radius:4px;
    --panel-pad:10px 12px;
    --row-pad:8px 12px;
  }

  /*
   * Font sets. Garden is :root, so it needs no block. `system` deliberately
   * names no web font at all — it downloads nothing and never flashes.
   */
  html[data-font="plain"]{--font-display:'Inter', sans-serif;--font-ui:'Inter', sans-serif;}
  html[data-font="legible"]{--font-display:'Atkinson Hyperlegible', sans-serif;--font-ui:'Atkinson Hyperlegible', sans-serif;}
  html[data-font="editorial"]{--font-display:'Source Serif 4', serif;--font-ui:'Source Sans 3', sans-serif;}
  html[data-font="system"]{--font-display:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;--font-ui:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;}
  html[data-font="slab"]{--font-display:'Zilla Slab', serif;--font-ui:'IBM Plex Sans', sans-serif;}
  html[data-font="storybook"]{--font-display:'EB Garamond', serif;--font-ui:'Lato', sans-serif;}
  html[data-font="rounded"]{--font-display:'Nunito', sans-serif;--font-ui:'Nunito', sans-serif;}
  html[data-font="condensed"]{--font-display:'Archivo Narrow', sans-serif;--font-ui:'Archivo', sans-serif;}
  html[data-font="technical"]{--font-display:'Space Grotesk', sans-serif;--font-ui:'IBM Plex Sans', sans-serif;}

  /* Ink and linen — cool light, navy ink. The plain counterpart to Garden paper. */
  html[data-theme="ink"]{
    --ink:#232A33; --ink-soft:#4A5462;
    --paper:#F4F4F1; --paper-raised:#FFFFFF;
    --line:#DCDCD6; --line-strong:#C2C2BA;
    --mustard:#A9761F; --mustard-soft:#F0E1C6;
    --sage:#5E8A6B; --sage-soft:#DEE9E1;
    --brick:#B23A3A; --brick-soft:#F3DCDC;
    --blue:#2C5F8A; --blue-soft:#D9E5EF;
    --green-dot:#3F7C4E; --yellow-dot:#B98F1E;
    --text-muted:#7D8590; --subtle-bg:#FAFAF8;
    --violet:#5B5FA6; --violet-soft:#E0E1F2;
    --plum:#8A4F77; --plum-soft:#EEDFE9;
    --teal:#2F8078; --teal-soft:#D8EAE7;
    --sage-ink:#2F4F39; --mustard-ink:#6E4A11; --blue-ink:#1E4160;
    --violet-ink:#383B75; --brick-ink:#7C2727; --plum-ink:#5E3550;
    --teal-ink:#235B55;
  }

  /* Moss — Garden paper with the colour turned up. */
  html[data-theme="moss"]{
    --ink:#16301F; --ink-soft:#375040;
    --paper:#EFF2EA; --paper-raised:#FDFEFB;
    --line:#D2DBC9; --line-strong:#B4C2A8;
    --mustard:#A9761B; --mustard-soft:#F1E2C3;
    --sage:#4E7F58; --sage-soft:#D8E7D9;
    --brick:#A83B34; --brick-soft:#F2DCD9;
    --blue:#2C6480; --blue-soft:#D7E6ED;
    --green-dot:#3B7A47; --yellow-dot:#B08B1C;
    --text-muted:#75856E; --subtle-bg:#F6F9F2;
    --violet:#5C5A9E; --violet-soft:#DFDEF0;
    --plum:#83486F; --plum-soft:#EDDDE7;
    --teal:#2A7B6B; --teal-soft:#D5E9E3;
    --sage-ink:#234A2C; --mustard-ink:#6B4A0F; --blue-ink:#1C4256;
    --violet-ink:#343267; --brick-ink:#6E241E; --plum-ink:#522B44;
    --teal-ink:#175044;
  }

  /* Newsprint — near-monochrome light. Colour appears only where it carries
     meaning, so status badges do more work. */
  html[data-theme="newsprint"]{
    --ink:#1A1A18; --ink-soft:#44443F;
    --paper:#F7F7F5; --paper-raised:#FFFFFF;
    --line:#DEDEDA; --line-strong:#BFBFB9;
    --mustard:#8F7031; --mustard-soft:#EFE6D5;
    --sage:#55705C; --sage-soft:#E2E7E2;
    --brick:#96453F; --brick-soft:#EEDFDD;
    --blue:#3C5C74; --blue-soft:#DFE6EB;
    --green-dot:#4A7355; --yellow-dot:#A8862A;
    --text-muted:#75756E; --subtle-bg:#FCFCFA;
    --violet:#55538A; --violet-soft:#E4E3EC;
    --plum:#7A5069; --plum-soft:#EBE1E7;
    --teal:#37716B; --teal-soft:#DEE9E7;
    --sage-ink:#2C3D31; --mustard-ink:#56421A; --blue-ink:#22384A;
    --violet-ink:#333155; --brick-ink:#5E2A26; --plum-ink:#4B2F40;
    --teal-ink:#1E443F;
  }

  /* Sandstone — warm neutral light. Garden paper without the green cast. */
  html[data-theme="sandstone"]{
    --ink:#332C24; --ink-soft:#574C40;
    --paper:#F4EFE6; --paper-raised:#FFFCF7;
    --line:#E2D9CB; --line-strong:#C9BCA8;
    --mustard:#A5741F; --mustard-soft:#F1E1C4;
    --sage:#6F8368; --sage-soft:#E3E8DC;
    --brick:#A9463A; --brick-soft:#F2DED9;
    --blue:#3E6479; --blue-soft:#DCE6EB;
    --green-dot:#4F7F55; --yellow-dot:#BE9425;
    --text-muted:#8B7F6E; --subtle-bg:#FAF6EF;
    --violet:#635A93; --violet-soft:#E4E1EF;
    --plum:#8B5271; --plum-soft:#EFDFE7;
    --teal:#357C71; --teal-soft:#D9E8E4;
    --sage-ink:#3B4A33; --mustard-ink:#684711; --blue-ink:#2A4453;
    --violet-ink:#3A3563; --brick-ink:#6F2A21; --plum-ink:#573044;
    --teal-ink:#1F4E46;
  }

  /* Slate — cool grey-blue dark. Replaces the old "dark" theme. */
  html[data-theme="slate"]{
    --ink:#E6EAF0; --ink-soft:#B9C2CD;
    --paper:#1B1F24; --paper-raised:#242A31;
    --line:#333B45; --line-strong:#46505C;
    --mustard:#D9A25A; --mustard-soft:#3D3320;
    --sage:#8FB09A; --sage-soft:#26332B;
    --brick:#E08078; --brick-soft:#3D2422;
    --blue:#6FA8D0; --blue-soft:#1F2E39;
    --green-dot:#6FC486; --yellow-dot:#E4BE58;
    --text-muted:#8994A1; --subtle-bg:#2C333B;
    --violet:#9B9BE0; --violet-soft:#2B2B40;
    --plum:#C08BB4; --plum-soft:#362534;
    --teal:#6BBDB4; --teal-soft:#1D3330;
    --sage-ink:#BCD4C4; --mustard-ink:#EBCB92; --blue-ink:#BBD9EC;
    --violet-ink:#C7C7EE; --brick-ink:#F0B4AC; --plum-ink:#E0BCD5;
    --teal-ink:#A8D9D2;
  }

  /* Dusk — deep indigo dark. */
  html[data-theme="dusk"]{
    --ink:#E4E3EE; --ink-soft:#B6B5C6;
    --paper:#15161F; --paper-raised:#1E2029;
    --line:#2E3040; --line-strong:#414459;
    --mustard:#DDA65E; --mustard-soft:#3B3120;
    --sage:#86B396; --sage-soft:#223026;
    --brick:#E58A83; --brick-soft:#3C2429;
    --blue:#7BAEE0; --blue-soft:#1E2C3E;
    --green-dot:#6BC384; --yellow-dot:#E5C264;
    --text-muted:#8A89A0; --subtle-bg:#262838;
    --violet:#A6A2EC; --violet-soft:#2A2846;
    --plum:#C892BC; --plum-soft:#34253A;
    --teal:#6FC3BC; --teal-soft:#1C3134;
    --sage-ink:#BDD8C6; --mustard-ink:#EFCE99; --blue-ink:#BEDBF1;
    --violet-ink:#CFCCF4; --brick-ink:#F2B8B2; --plum-ink:#E4C2DC;
    --teal-ink:#ABDDD8;
  }

  /* Ember — warm dark. The only one of the three darks without a cool cast. */
  html[data-theme="ember"]{
    --ink:#F0E7DE; --ink-soft:#CBBCAF;
    --paper:#1E1A17; --paper-raised:#292421;
    --line:#3D3530; --line-strong:#504640;
    --mustard:#E0A455; --mustard-soft:#45341D;
    --sage:#9CB08C; --sage-soft:#2C3527;
    --brick:#E5867A; --brick-soft:#432723;
    --blue:#86AEC4; --blue-soft:#263239;
    --green-dot:#83C48F; --yellow-dot:#E8C46A;
    --text-muted:#968779; --subtle-bg:#332C28;
    --violet:#B0A0D4; --violet-soft:#322C40;
    --plum:#C58FA8; --plum-soft:#3A2A32;
    --teal:#6FBDAE; --teal-soft:#21332F;
    --sage-ink:#C8D8BC; --mustard-ink:#F2CE96; --blue-ink:#BDD6E3;
    --violet-ink:#D6CCEC; --brick-ink:#F4B7AE; --plum-ink:#E5C3D2;
    --teal-ink:#ADDBD1;
  }

  /* Obsidian — near-black neutral. The highest-contrast of the darks. */
  html[data-theme="obsidian"]{
    --ink:#F2F3F5; --ink-soft:#C3C6CC;
    --paper:#0E0F11; --paper-raised:#17191C;
    --line:#282B30; --line-strong:#3A3E45;
    --mustard:#E5AE63; --mustard-soft:#3A2F1C;
    --sage:#93BCA0; --sage-soft:#1D2A21;
    --brick:#EC8B81; --brick-soft:#3A2220;
    --blue:#7DB6E2; --blue-soft:#182833;
    --green-dot:#79CE90; --yellow-dot:#EBC85E;
    --text-muted:#909499; --subtle-bg:#1D2024;
    --violet:#A9A6F0; --violet-soft:#242347;
    --plum:#D096C2; --plum-soft:#331F30;
    --teal:#71C8BE; --teal-soft:#152E2B;
    --sage-ink:#C3DDCB; --mustard-ink:#F3D3A0; --blue-ink:#C4DEF3;
    --violet-ink:#D3D1F7; --brick-ink:#F6BCB4; --plum-ink:#EAC6E0;
    --teal-ink:#AFE0D9;
  }

  /* Forest — deep green dark, the green-black the retired Nightshade had. */
  html[data-theme="forest"]{
    --ink:#E7EFE4; --ink-soft:#BCCBB7;
    --paper:#101A13; --paper-raised:#19251C;
    --line:#2A3A2C; --line-strong:#3C4E3E;
    --mustard:#D6A356; --mustard-soft:#3C3320;
    --sage:#8CBE97; --sage-soft:#213022;
    --brick:#E28B7E; --brick-soft:#3B2523;
    --blue:#7FB3CC; --blue-soft:#1B2F37;
    --green-dot:#71CB86; --yellow-dot:#E1C25D;
    --text-muted:#849081; --subtle-bg:#1E2C21;
    --violet:#A29ED8; --violet-soft:#292B40;
    --plum:#C08FAF; --plum-soft:#372936;
    --teal:#6BC0B0; --teal-soft:#173330;
    --sage-ink:#BFDBC6; --mustard-ink:#EDCE97; --blue-ink:#BFDAE9;
    --violet-ink:#CBC8EE; --brick-ink:#F1B9AF; --plum-ink:#E2C1D6;
    --teal-ink:#A7DCD2;
  }
  html[data-scheme="dark"] .badge.pending{color:#E8C98A;}
  html[data-scheme="dark"] .badge.fulfilled{color:#BFE0C6;}
  html[data-scheme="dark"] .badge.paid{color:#12281A;}
  html[data-scheme="dark"] .badge.consigned{color:#BFDCEA;}
  html[data-scheme="dark"] .badge.st-waiting{color:#E8C98A;}
  html[data-scheme="dark"] .badge.st-progress{color:#BFDCEA;}
  html[data-scheme="dark"] .badge.st-done{color:#BFE0C6;}
  html[data-scheme="dark"] .badge.sentiment-not{color:#F0B7AE;}
  html[data-scheme="dark"] .badge.sentiment-follow{color:#BFDCEA;}
  html[data-scheme="dark"] .badge.sentiment-nurture{color:#BFE0C6;}
  html[data-scheme="dark"] .badge.warn{color:#F0B7AE;}
  html[data-scheme="dark"] .badge.sentiment-connected{color:#12281A;}
  body{transition:background-color 0.15s ease, color 0.15s ease;}
  *{box-sizing:border-box;}
  body{margin:0;background:var(--paper);color:var(--ink);font-family:var(--font-ui);-webkit-font-smoothing:antialiased;}
  .app{max-width:1180px;margin:0 auto;display:grid;grid-template-columns:220px 1fr;min-height:100vh;}
  @media (max-width:820px){ .app{grid-template-columns:1fr;} .rail{position:sticky;top:0;z-index:30;} }

  .rail{border-right:1px solid var(--line);padding:28px 18px;display:flex;flex-direction:column;gap:18px;background:var(--paper);}
  @media (max-width:820px){ .rail{border-right:none;border-bottom:1px solid var(--line);flex-direction:row;align-items:center;padding:14px 18px;gap:14px;} }

  .brand{display:flex;align-items:baseline;gap:8px;}
  .brand-mark{font-family:var(--font-display);font-weight:600;font-size:21px;letter-spacing:-0.01em;}
  .brand-sub{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);letter-spacing:0.08em;text-transform:uppercase;margin-top:-12px;}
  @media (max-width:820px){ .brand-sub{display:none;} .brand{flex-shrink:0;} .brand-mark{font-size:17px;} }

  .tabs{display:flex;flex-direction:column;gap:2px;flex:1;}
  @media (max-width:820px){ .tabs{flex-direction:row;flex:1;flex-wrap:wrap;} }
  .tab{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;border-radius:var(--radius);font-size:14px;color:var(--ink-soft);cursor:pointer;white-space:nowrap;border:1px solid transparent;transition:background 0.12s ease;}
  .tab:hover{background:var(--sage-soft);}
  .tab.active{background:var(--paper-raised);border-color:var(--line);color:var(--ink);font-weight:500;}
  .tab .count{font-family:var(--font-mono);font-size:11px;color:var(--text-muted);}
  .tab.active .count{color:var(--mustard);}

  /* Outline icons, drawn from the sprite in index.html.
     `stroke:currentColor` is the whole reason this works across ten themes and
     five dark ones: the icon inherits .tab's colour, so it lightens on hover
     and darkens with .tab.active without a single extra rule. Never give an
     icon its own colour here or that stops being true.
     A .tab is display:flex with space-between, so the icon and label have to
     share ONE flex child (.tab-label) or the count gets pushed into the middle. */
  .ic{width:17px;height:17px;flex-shrink:0;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;}
  .tab-label{display:flex;align-items:center;gap:9px;min-width:0;overflow:hidden;text-overflow:ellipsis;}
  /* Buttons are inline rather than flex, so the icon is aligned optically
     against the text baseline instead of being centred by the layout. */
  .btn .ic{vertical-align:-3.5px;margin-right:5px;width:15px;height:15px;}
  .page-title .ic{width:21px;height:21px;vertical-align:-3px;margin-right:8px;}

  .tab-group{position:relative;}
  .submenu{display:none;position:absolute;left:100%;top:0;margin-left:6px;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;min-width:170px;padding:4px;z-index:40;box-shadow:0 4px 14px rgba(31,58,52,0.12);}
  .tab-group:hover .submenu{display:block;}
  @media (max-width:820px){ .submenu{left:0;top:100%;margin-left:0;margin-top:4px;} }
  .submenu-item{padding:8px 10px;border-radius:5px;font-size:13px;color:var(--ink-soft);cursor:pointer;display:flex;align-items:center;gap:8px;white-space:nowrap;}
  .submenu-item:hover{background:var(--sage-soft);color:var(--ink);}
  .submenu-item .count{margin-left:auto;}

  /*
   * Theme picker. Each swatch has to show ITS OWN colours, not the active
   * theme's — a `data-theme` attribute on an element inside the page doesn't
   * pick up the html[data-theme=…] blocks, so the four colours each swatch
   * needs are restated here. Keep them in step with the theme blocks above; a
   * swatch that lies about a theme is worse than no swatch.
   */
  /* Five across, two down. The count is fixed rather than auto-fit because
     auto-fit lands on three or four columns at some widths, and ten themes
     across three columns leaves a ragged last row — which is the thing this
     grid exists to avoid. Both 5 and 2 divide ten exactly. */
  /* The sample under the font dropdown. Always rendered in the current choice,
     because browsers vary in how faithfully they style <option> text and the
     dropdown alone can't be trusted to show the difference. */
  .font-sample{display:flex;flex-direction:column;gap:3px;margin-top:12px;padding:12px 14px;background:var(--subtle-bg);border:1px solid var(--line);border-radius:var(--radius);}
  .font-sample-display{font-family:var(--font-display);font-size:19px;font-weight:600;color:var(--ink);}
  .font-sample-ui{font-family:var(--font-ui);font-size:13px;color:var(--ink-soft);}
  .font-sample-mono{font-family:var(--font-mono);font-size:12px;color:var(--text-muted);}

  /* Five styles: 5 columns or 1, both of which divide five exactly. */
  /* Event signup sharing: the QR beside the link, stacking on a narrow screen. */
  .signup-share{display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start;}
  .signup-qr-wrap{display:flex;flex-direction:column;gap:8px;align-items:center;flex-shrink:0;}
  /* White always, whatever the theme — a QR inverted on a dark background is
     unreliable to scan, and this one gets printed. */
  #signup-qr{background:#fff;padding:10px;border:1px solid var(--line);border-radius:var(--radius);line-height:0;}
  #signup-qr img{display:block;image-rendering:pixelated;}
  .signup-share-info{flex:1;min-width:230px;}
  .signup-share-info input{width:100%;}
  .row.events-signups{grid-template-columns:1.6fr 1fr 1.1fr auto;}

  /* Photo library. auto-fill rather than auto-fit so a single photo stays a
     card instead of stretching across the whole row. */
  /* Large is the original. Small drops the SKU line and tags too — at 96px
     they are unreadable, and a wall of truncated text is worse than none. */
  .photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;}
  .photo-grid[data-size="medium"]{grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;}
  .photo-grid[data-size="small"]{grid-template-columns:repeat(auto-fill,minmax(84px,1fr));gap:8px;}
  .photo-grid[data-size="small"] .photo-card{padding:5px;gap:2px;}
  .photo-grid[data-size="small"] .photo-card-name{font-size:10px;}
  .ph-size-toggle{display:inline-flex;border:1px solid var(--line-strong);border-radius:var(--radius);overflow:hidden;}
  .ph-size-btn{font:inherit;font-size:12px;padding:7px 11px;border:none;background:var(--paper-raised);color:var(--ink-soft);cursor:pointer;}
  .ph-size-btn + .ph-size-btn{border-left:1px solid var(--line);}
  .ph-size-btn.active{background:var(--sage-soft);color:var(--sage-ink);font-weight:500;}
  .photo-card{position:relative;display:flex;flex-direction:column;gap:4px;text-align:left;background:var(--panel-bg);border:var(--panel-border);border-radius:var(--panel-radius);box-shadow:var(--panel-shadow);padding:8px;cursor:pointer;font:inherit;color:inherit;}
  /* Multi-select. position:relative on the card is what the tick anchors to;
     an outline rather than a border so selecting does not resize the card and
     reflow the whole grid. */
  .photo-card.selected{outline:2px solid var(--blue);outline-offset:-2px;}
  .photo-pick{position:absolute;top:6px;left:6px;width:18px;height:18px;border-radius:4px;
    border:1px solid var(--line-strong);background:var(--paper-raised);color:#fff;
    font-size:12px;line-height:16px;text-align:center;z-index:2;}
  .photo-pick.on{background:var(--blue);border-color:var(--blue);}
  .photo-card:hover{border-color:var(--line-strong);}
  .photo-card img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:4px;background:var(--subtle-bg);display:block;}
  .photo-card-name{font-size:12px;font-weight:500;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .photo-card-meta{font-family:var(--font-mono);font-size:10px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .photo-tags{display:flex;flex-wrap:wrap;gap:3px;margin-top:2px;}
  .photo-tag{font-size:10px;padding:1px 6px;border-radius:8px;background:var(--sage-soft);color:var(--sage-ink);}

  .block-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;}
  @media (max-width:900px){ .block-grid{grid-template-columns:repeat(1,1fr);} }
  /*
   * The block preview draws its own treatment rather than inheriting the
   * active one — the same reason the theme swatches restate their colours.
   * These are literal values, not the --panel-* variables, or every card would
   * show whichever style is already applied.
   */
  .block-preview{display:block;background:var(--paper);border-radius:5px;padding:7px;height:52px;}
  .block-preview-panel{display:flex;flex-direction:column;gap:4px;height:100%;background:var(--paper-raised);border:1px solid var(--line);border-radius:6px;padding:7px 8px;}
  .block-preview-title{display:block;height:6px;width:52%;border-radius:2px;background:var(--ink-soft);}
  .block-preview-line{display:block;height:4px;border-radius:2px;background:var(--line-strong);}
  .block-preview-line.short{width:60%;}
  .block-preview[data-blocks="flat"] .block-preview-panel{background:var(--subtle-bg);border-color:transparent;}
  .block-preview[data-blocks="elevated"] .block-preview-panel{border-color:transparent;border-radius:9px;box-shadow:0 2px 6px rgba(31,58,52,0.16);}
  html[data-scheme="dark"] .block-preview[data-blocks="elevated"] .block-preview-panel{box-shadow:0 2px 7px rgba(0,0,0,0.5);}
  .block-preview[data-blocks="ruled"] .block-preview-panel{background:transparent;border:none;border-radius:0;padding:0;}
  .block-preview[data-blocks="ruled"] .block-preview-title{border-bottom:1px solid var(--line);padding-bottom:4px;height:10px;width:100%;background:transparent;border-radius:0;}
  .block-preview[data-blocks="compact"] .block-preview-panel{border-radius:3px;padding:4px 5px;gap:3px;}

  .theme-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;}
  @media (max-width:900px){ .theme-grid{grid-template-columns:repeat(2,1fr);} }
  .theme-card{display:flex;flex-direction:column;gap:6px;text-align:left;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;padding:10px;cursor:pointer;font:inherit;color:inherit;}
  .theme-card:hover{border-color:var(--line-strong);}
  /* 2px so the selected card doesn't shift by a pixel when it becomes selected. */
  .theme-card.selected{border:2px solid var(--sage);padding:9px;}
  .theme-card-name{font-size:13px;font-weight:500;color:var(--ink);}
  .theme-card-tick{color:var(--green-dot);}
  .theme-card-note{font-size:11px;color:var(--text-muted);line-height:1.4;}
  .theme-swatch{display:flex;gap:4px;height:42px;border-radius:5px;border:1px solid var(--line);padding:5px;background:var(--sw-paper);}
  .theme-swatch-rail{width:26px;border-radius:3px;background:var(--sw-rail);}
  .theme-swatch-card{flex:1;border-radius:3px;background:var(--sw-card);border:1px solid var(--sw-line);}
  .theme-swatch[data-theme="garden"]{--sw-paper:#F5F1E8;--sw-rail:#E4E9DF;--sw-card:#FFFFFF;--sw-line:#DAD3C2;}
  .theme-swatch[data-theme="ink"]{--sw-paper:#F4F4F1;--sw-rail:#DEE9E1;--sw-card:#FFFFFF;--sw-line:#DCDCD6;}
  .theme-swatch[data-theme="moss"]{--sw-paper:#EFF2EA;--sw-rail:#D8E7D9;--sw-card:#FDFEFB;--sw-line:#D2DBC9;}
  .theme-swatch[data-theme="slate"]{--sw-paper:#1B1F24;--sw-rail:#26332B;--sw-card:#242A31;--sw-line:#333B45;}
  .theme-swatch[data-theme="dusk"]{--sw-paper:#15161F;--sw-rail:#223026;--sw-card:#1E2029;--sw-line:#2E3040;}
  .theme-swatch[data-theme="ember"]{--sw-paper:#1E1A17;--sw-rail:#2C3527;--sw-card:#292421;--sw-line:#3D3530;}
  .theme-swatch[data-theme="newsprint"]{--sw-paper:#F7F7F5;--sw-rail:#E2E7E2;--sw-card:#FFFFFF;--sw-line:#DEDEDA;}
  .theme-swatch[data-theme="sandstone"]{--sw-paper:#F4EFE6;--sw-rail:#E3E8DC;--sw-card:#FFFCF7;--sw-line:#E2D9CB;}
  .theme-swatch[data-theme="obsidian"]{--sw-paper:#0E0F11;--sw-rail:#1D2A21;--sw-card:#17191C;--sw-line:#282B30;}
  .theme-swatch[data-theme="forest"]{--sw-paper:#101A13;--sw-rail:#213022;--sw-card:#19251C;--sw-line:#2A3A2C;}

  /* Address check feedback under the city/state/zip row. Deliberately quiet —
     it is advice, not a validation error, and must not read like one. */
  .address-check{background:var(--subtle-bg);border:1px solid var(--line);border-radius:var(--radius);padding:10px 12px;margin:-4px 0 14px;}
  .address-check-actions{display:flex;gap:8px;margin-top:8px;}
  .address-check-note{font-size:12px;color:var(--text-muted);margin:-4px 0 12px;}
  .address-check .address-check-note{margin:0;color:var(--ink-soft);}
  .address-check-note.ok{color:var(--green-dot);}
  .address-check-note.warn{color:var(--mustard);}

  .nav-category{display:flex;flex-direction:column;}
  /* A group head is a filled band; the tabs inside it are not. That contrast
     is the whole point — the rail carries seven groups and they were reading
     as just more tabs. --cat-tint/--cat-accent are set per group below, so
     this rule never needs to know which colour it is wearing. */
  .nav-category-head{display:flex;justify-content:space-between;align-items:center;padding:7px 10px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;background:var(--cat-tint,transparent);color:var(--cat-ink,var(--text-muted));cursor:pointer;border-radius:var(--radius);transition:box-shadow 0.12s ease;}
  /* Hover draws a ring rather than changing the fill: a second tint per
     family would be seven more tokens in two themes, and the ring reads as
     affordance without shifting the colour somebody just learned. */
  .nav-category-head:hover{box-shadow:inset 0 0 0 1px var(--cat-accent,var(--line-strong));}
  .nav-category-head[data-cat="crm"]{--cat-tint:var(--sage-soft);--cat-ink:var(--sage-ink);--cat-accent:var(--sage);}
  /* Gnome Books groups. Ledger takes sage (the first family, the CRM group's
     colour there); the CRM's own group keys never render in this app, so
     there is no collision. Later groups (AR/AP, Banking, Reports) claim the
     remaining families as they arrive. */
  .nav-category-head[data-cat="ledger"]{--cat-tint:var(--sage-soft);--cat-ink:var(--sage-ink);--cat-accent:var(--sage);}
  .nav-category-head[data-cat="products"]{--cat-tint:var(--mustard-soft);--cat-ink:var(--mustard-ink);--cat-accent:var(--mustard);}
  .nav-category-head[data-cat="documents"]{--cat-tint:var(--blue-soft);--cat-ink:var(--blue-ink);--cat-accent:var(--blue);}
  .nav-category-head[data-cat="apps"]{--cat-tint:var(--violet-soft);--cat-ink:var(--violet-ink);--cat-accent:var(--violet);}
  .nav-category-head[data-cat="payroll"]{--cat-tint:var(--brick-soft);--cat-ink:var(--brick-ink);--cat-accent:var(--brick);}
  .nav-category-head[data-cat="marketing"]{--cat-tint:var(--plum-soft);--cat-ink:var(--plum-ink);--cat-accent:var(--plum);}
  .nav-category-head[data-cat="portal"]{--cat-tint:var(--teal-soft);--cat-ink:var(--teal-ink);--cat-accent:var(--teal);}
  /* Neutral, not an eighth colour family. The palette has exactly seven usable
     families and they are all spoken for; adding one would mean two new tokens
     in each of the ten theme blocks. Admin is chrome rather than a feature
     area, so reading as grey is apt rather than a compromise. These three
     tokens exist in every theme already. */
  .nav-category-head[data-cat="admin"]{--cat-tint:var(--subtle-bg);--cat-ink:var(--ink-soft);--cat-accent:var(--line-strong);}
  .nav-caret{transition:transform 0.15s ease;font-size:10px;}
  .nav-caret.open{transform:rotate(90deg);}
  .nav-category-body{display:none;flex-direction:column;gap:2px;}
  .nav-category-body.open{display:flex;}
  @media (max-width:820px){ .nav-category-body.open{flex-direction:row;flex-wrap:wrap;} }

  .dropdown-widget{position:relative;}
  .dropdown-panel{display:none;position:absolute;left:100%;top:0;margin-left:6px;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;width:320px;max-height:460px;overflow-y:auto;z-index:40;box-shadow:0 4px 14px rgba(31,58,52,0.14);padding:12px;}
  .dropdown-panel.open{display:block;}
  @media (max-width:820px){ .dropdown-panel{left:auto;right:0;top:100%;margin-left:0;margin-top:6px;} }
  .dropdown-panel-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;gap:8px;}
  .dropdown-panel-title{font-family:var(--font-display);font-size:15px;font-weight:600;}

  .rail-foot{font-size:11px;color:var(--text-muted);font-family:var(--font-mono);}
  @media (max-width:820px){ .rail-foot{display:none;} }

  .main{padding:32px 36px 60px;}
  @media (max-width:820px){ .main{padding:20px 18px 48px;} }

  .page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:22px;flex-wrap:wrap;}
  .page-title{font-family:var(--font-display);font-size:28px;font-weight:600;margin:0;}
  .page-sub{font-size:13px;color:var(--ink-soft);margin:4px 0 0;}
  .back-link{font-size:12px;color:var(--ink-soft);cursor:pointer;display:inline-flex;align-items:center;gap:4px;margin-bottom:10px;}
  .back-link:hover{color:var(--ink);}

  .btn{font-family:var(--font-ui);font-size:13px;font-weight:500;padding:9px 16px;border-radius:var(--radius);border:1px solid var(--ink);background:var(--ink);color:var(--paper);cursor:pointer;}
  .btn:hover{background:var(--ink-soft);}
  .btn.ghost{background:transparent;color:var(--ink);border-color:var(--line-strong);}
  .btn.ghost:hover{background:var(--paper-raised);}
  .btn.danger{background:transparent;color:var(--brick);border-color:var(--brick);}
  .btn.danger:hover{background:var(--brick-soft);}
  .btn.small{padding:5px 10px;font-size:12px;}
  .btn:disabled{opacity:0.4;cursor:default;}

  .collapse-toggle{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border-radius:var(--radius);border:1px solid var(--line-strong);background:transparent;color:var(--ink);cursor:pointer;}
  .collapse-toggle:hover{background:var(--paper-raised);}
  .collapse-arrow{display:inline-block;font-size:13px;line-height:1;transition:transform .15s ease;transform:rotate(-90deg);}
  .collapse-arrow.expanded{transform:rotate(0deg);}

  .toolbar{display:flex;gap:10px;align-items:center;margin:16px 0 18px;flex-wrap:wrap;}
  input[type=text], input[type=email], input[type=password], input[type=tel], input[type=date], input[type=time], input[type=number], input[type=url], textarea, select{
    font-family:var(--font-ui);font-size:13px;padding:8px 10px;border:1px solid var(--line-strong);border-radius:var(--radius);background:var(--paper-raised);color:var(--ink);
  }
  textarea{font-family:var(--font-ui);resize:vertical;}
  input:focus, textarea:focus, select:focus{outline:none;border-color:var(--mustard);box-shadow:0 0 0 2px var(--mustard-soft);}
  input.search{
    flex:1;min-width:180px;padding-right:32px;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23808080' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
    background-repeat:no-repeat;background-position:right 10px center;background-size:16px 16px;
  }

  .price-wrap{display:flex;align-items:center;border:1px solid var(--line-strong);border-radius:var(--radius);background:var(--paper-raised);padding:0 8px;}
  .price-wrap span{color:var(--ink-soft);font-size:13px;}
  .price-wrap input{border:none;background:transparent;padding:8px 4px;width:100%;-webkit-appearance:none;appearance:none;}
  .price-wrap input:focus{outline:none;box-shadow:none;border:none;}
  .price-wrap:focus-within{border-color:var(--mustard);box-shadow:0 0 0 2px var(--mustard-soft);}
  .price-wrap input::-webkit-inner-spin-button, .price-wrap input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0;}
  .price-wrap input[type=number]{-moz-appearance:textfield;}

  .modal-header-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:18px;gap:12px;flex-wrap:wrap;}
  .modal-header-row h2{margin:0;}
  .inline-checkbox{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:500;cursor:pointer;white-space:nowrap;}
  .inline-checkbox input{width:16px;height:16px;padding:0;}
  .checkbox-group{display:flex;gap:14px;flex-wrap:wrap;}

  .panel{background:var(--panel-bg);border:var(--panel-border);border-radius:var(--panel-radius);padding:var(--panel-pad);box-shadow:var(--panel-shadow);}
  .card-list{display:flex;flex-direction:column;border:var(--panel-border);border-radius:var(--panel-radius);overflow:hidden;background:var(--panel-bg);box-shadow:var(--panel-shadow);}
  .row{display:grid;gap:12px;align-items:center;padding:var(--row-pad);border-bottom:1px solid var(--line);cursor:pointer;}
  .row:last-child{border-bottom:none;}
  .row:hover{background:var(--subtle-bg);}
  .row.head{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);cursor:default;}
  .row.head:hover{background:transparent;}
  .row.head .sortable{cursor:pointer;user-select:none;}
  .row.head .sortable:hover{color:var(--ink);}
  /* Every fraction is minmax(0,...) because each .row is its own grid
     container, so a bare fr floors at that row's min-content width and a long
     company name resolves its columns differently from its neighbours. */
  .row.companies{grid-template-columns:18px minmax(0,1.5fr) minmax(0,1.1fr) minmax(0,0.6fr) minmax(0,0.8fr) minmax(0,0.8fr);}
  /* A soft box for the last-activity cell. Not .badge: that carries a status
     colour, and "we spoke a fortnight ago" is neither good nor bad news. */
  .pill-soft{display:inline-block;font-size:11px;padding:2px 8px;border-radius:20px;
    background:var(--paper-raised);border:1px solid var(--line);color:var(--ink-soft);}
  /* Fixed rather than auto so the ticks line up column-wise — an auto column
     collapses on rows without one and leaves the names ragged. */
  .row.companies .portal-check{color:var(--green-dot);font-size:13px;line-height:1;}
  .row.companies .primary-text,
  .row.companies .sub-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px;}
  .row.contacts{grid-template-columns:1.3fr 1fr 1.1fr 0.9fr auto;}
  .row.touches{grid-template-columns:0.8fr 0.9fr 1fr 0.8fr 1.4fr;}
  .row.products{grid-template-columns:52px 2.1fr 0.4fr 0.6fr 0.7fr auto;}
  .row.products .primary-text,
  .row.products .sub-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px;}
  /* Type | contact over company | local time | date | actions.
     Two rules are load-bearing here. Every fraction is minmax(0,…) because each
     .row is its own grid container, so a bare fr floors at that row's
     min-content width and rows with a longer company name resolve their columns
     differently. And the last track is a FIXED width, not auto, because Email
     rows carry two buttons where the others carry one — auto sized to content
     and dragged every column left on exactly those rows. */
  .row.cadence{grid-template-columns:minmax(0,0.7fr) minmax(0,1.6fr) 60px minmax(0,0.9fr) 148px;}
  .cadence-who{display:flex;flex-direction:column;gap:1px;min-width:0;}
  .cadence-who span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .cadence-who .sub-text{font-size:12px;}
  .row.production-needed .primary-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px;}
  .row.inventory-onhand .primary-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px;}
  .row.invoices{grid-template-columns:1fr 1fr 0.85fr 0.85fr 0.75fr 0.8fr auto;}
  .row.events{grid-template-columns:1.6fr 1.1fr 0.9fr 0.6fr;}
  .row.events-items{grid-template-columns:1.8fr 0.6fr 0.8fr 0.8fr;cursor:default;}
  .row.events-items:hover{background:transparent;}
  .row.events-staff{grid-template-columns:1.4fr 0.9fr 1fr;cursor:default;}
  .row.events-staff:hover{background:transparent;}
  .row.shortfall{grid-template-columns:1.2fr 0.9fr 0.8fr 0.8fr auto;cursor:default;}
  .row.shortfall:hover{background:transparent;}
  .event-staff-row{display:grid;grid-template-columns:1.5fr 1fr 0.7fr 0.7fr auto;gap:8px;align-items:center;margin-bottom:8px;}
  .line-item-row.event-line{grid-template-columns:1.9fr 0.3fr 0.6fr 0.7fr auto;}
  .row.employees{grid-template-columns:1.6fr 0.9fr 0.8fr 1fr 0.7fr;}
  .row.w4{grid-template-columns:0.8fr 1.4fr 1fr 1fr;cursor:default;}
  .row.w4:hover{background:transparent;}
  .row.pay-schedules{grid-template-columns:1.4fr 1fr 1.1fr 0.8fr auto;cursor:default;}
  .row.pay-schedules:hover{background:transparent;}
  .row.portal-request{grid-template-columns:1fr auto;align-items:start;cursor:default;padding:14px;}
  .row.portal-request:hover{background:transparent;}
  .row.subscribers{grid-template-columns:1.5fr 0.9fr 1fr 1fr 0.8fr 0.9fr 0.8fr;}
  /* One line per subscriber. Without this a long address or a stack of tags
     makes rows different heights and the list stops being scannable. */
  .row.subscribers > div{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .row.compliance-check{grid-template-columns:1fr auto;align-items:start;cursor:default;padding:12px 14px;}
  .row.compliance-check:hover{background:transparent;}
  .row.liabilities{grid-template-columns:1.8fr 1.3fr 0.9fr 0.9fr 1.2fr;cursor:default;}
  .row.liabilities:hover{background:transparent;}
  .row.report-line{grid-template-columns:2.2fr 0.8fr;cursor:default;padding:7px 14px;}
  .row.report-line:hover{background:transparent;}
  .row.report-line.strong{background:var(--subtle-bg);}
  .row.w2-row{grid-template-columns:1.6fr 0.9fr 0.9fr 0.9fr 0.9fr;cursor:default;}
  .row.w2-row:hover{background:transparent;}
  .row.nec-row{grid-template-columns:1.6fr 1fr 1fr;cursor:default;}
  .row.nec-row:hover{background:transparent;}
  .row.employee-deductions{grid-template-columns:1.3fr 1.1fr 1.2fr 1.1fr auto;cursor:default;}
  .row.employee-deductions:hover{background:transparent;}
  .row.pay-runs{grid-template-columns:1.6fr 1fr 0.6fr 0.9fr 0.9fr 0.8fr;}
  .row.pay-run-lines{grid-template-columns:1.5fr 1fr 0.9fr 0.9fr 0.9fr auto;cursor:default;}
  .row.pay-run-lines:hover{background:transparent;}
  .row.paystub-line{grid-template-columns:1.4fr 1fr 0.8fr 34px;cursor:default;padding:7px 14px;}
  .row.paystub-line:hover{background:transparent;}
  .row.paystub-line.strong{background:var(--subtle-bg);}
  .row.preview-line{grid-template-columns:1.6fr 0.8fr;cursor:default;padding:7px 14px;}
  .row.preview-line:hover{background:transparent;}
  .row.preview-line.strong{background:var(--subtle-bg);}
  .row.time-entries{grid-template-columns:34px 0.9fr 1.1fr 0.6fr 0.9fr 1.1fr 1.2fr;cursor:default;}
  .row.time-entries:hover{background:transparent;}
  .row.time-entries-total{display:grid;grid-template-columns:34px 0.9fr 1.1fr 0.6fr 0.9fr 1.1fr 1.2fr;padding:10px 14px;border-top:1px solid var(--line-strong);background:var(--subtle-bg);}

  .calendar-toolbar{flex-wrap:wrap;gap:8px;align-items:center;}
  .calendar-toggles{display:flex;gap:14px;align-items:center;margin-left:auto;flex-wrap:wrap;}
  .calendar-toggles .inline-checkbox{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--ink-soft);white-space:nowrap;}
  .cal-key{display:inline-block;width:10px;height:10px;border-radius:3px;flex-shrink:0;}
  .cal-tooltip{display:none;position:absolute;z-index:200;max-width:280px;background:var(--paper-raised);border:1px solid var(--line-strong);border-radius:var(--radius);box-shadow:0 6px 20px rgba(0,0,0,0.14);padding:8px 11px;font-size:12px;line-height:1.5;color:var(--ink);pointer-events:none;}
  .cal-tip-item{color:var(--ink-soft);padding-left:9px;}
  .cal-tip-sep{border-top:1px solid var(--line);margin:6px 0;}
  .color-swatches{display:flex;gap:8px;}
  .color-swatch{width:26px;height:26px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;}
  .color-swatch.selected{border-color:var(--ink);}
  .cal-c-blue{background:var(--blue);color:#FFFFFF;}
  .cal-c-sage{background:var(--sage);color:#12281A;}
  .cal-c-mustard{background:var(--mustard);color:#FFFFFF;}
  .cal-c-brick{background:var(--brick);color:#FFFFFF;}
  .cal-c-purple{background:#7A5C99;color:#FFFFFF;}
  .cal-c-grey{background:var(--text-muted);color:#FFFFFF;}
  .calendar-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:8px;overflow:hidden;}
  .calendar-dow{background:var(--subtle-bg);padding:7px 4px;font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);text-align:center;}
  .calendar-cell{background:var(--paper-raised);min-height:96px;padding:6px;overflow:hidden;}
  .calendar-cell.other-month{background:var(--subtle-bg);}
  .calendar-day-num{font-family:var(--font-mono);font-size:12px;color:var(--ink-soft);margin-bottom:4px;}
  .calendar-cell.today .calendar-day-num{display:inline-block;background:var(--ink);color:var(--paper);border-radius:10px;padding:1px 7px;}
  .cal-pill{display:block;font-size:11px;line-height:1.6;border-radius:9px;padding:1px 7px;margin-top:3px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .cal-pill:hover{filter:brightness(0.95);}
  .cal-orders{background:var(--brick-soft);color:#8C332E;}
  .cal-todos{background:var(--yellow-dot);color:#3A2E08;}
  .cal-event{background:var(--green-dot);color:#12281A;}
  /* Violet, because the four existing markers already hold red, yellow, green
     and the custom-event palette. Its text comes from the same hue family, the
     rule the nav tints follow: --ink on a coloured fill reads as a mistake. */
  .cal-social{background:var(--violet-soft);color:#33336B;}
  /* Business filing deadlines. Neutral rather than a new colour family (the
     palette's seven are spoken for, the Admin-tint rule); filed ones fade so a
     done filing stops asking for attention. */
  .cal-compliance{background:var(--line);color:var(--ink);}
  .cal-compliance.filed{opacity:.55;text-decoration:line-through;}
  html[data-scheme="dark"] .cal-orders{color:#F3DEDB;}
  html[data-scheme="dark"] .cal-social{color:#E3E2F2;}
  @media (max-width:760px){
    .calendar-cell{min-height:54px;padding:3px;}
    .cal-pill{width:9px;height:9px;padding:0;margin:2px 2px 0 0;border-radius:50%;display:inline-block;font-size:0;line-height:0;}
  }
  @media (max-width:760px){ .row.companies,.row.contacts,.row.touches,.row.products,.row.cadence,.row.invoices,.row.events,.row.events-items,.row.events-staff,.row.shortfall{grid-template-columns:1fr;gap:4px;} .row.head{display:none;} .event-staff-row{grid-template-columns:1fr;} .line-item-row.event-line{grid-template-columns:1.6fr 0.4fr 0.7fr auto;} .row.employees,.row.w4,.row.pay-schedules,.row.time-entries,.row.time-entries-total,.row.portal-request,.row.subscribers,.row.pay-runs,.row.pay-run-lines,.row.paystub-line,.row.liabilities,.row.report-line,.row.w2-row,.row.nec-row,.row.employee-deductions{grid-template-columns:1fr;gap:4px;} }

  .primary-text{font-weight:500;}
  .sub-text{color:var(--ink-soft);font-size:13px;}
  .micro-text{color:var(--text-muted);font-size:11px;}
  .empty{padding:40px 20px;text-align:center;color:var(--ink-soft);}
  .empty h3{font-family:var(--font-display);font-size:19px;margin:0 0 6px;color:var(--ink);}
  .empty p{font-size:13px;margin:0 0 16px;}

  /* nowrap because a status label broken across two lines reads as two
     words rather than one state, and it makes its row taller than its
     neighbours, which is what makes a list look broken. */
  .badge{display:inline-block;font-size:11px;font-weight:500;padding:3px 9px;border-radius:20px;white-space:nowrap;}
  .badge.pending{background:var(--mustard-soft);color:#8A5A1E;}
  .badge.fulfilled{background:var(--sage-soft);color:#3E5340;}
  .badge.paid{background:var(--green-dot);color:#12281A;}
  .badge.consigned{background:var(--blue-soft);color:#2E5570;}
  /* Where a record moves through stages, in the same tinted language as the
     badges above: soft ground, text from the SAME hue. Deliberately generic
     rather than one family per feature, because work orders and purchase
     orders ask the same four questions and two copies of these tints would
     drift. Waiting shares Pending's mustard; progress takes blue, the only
     family here that reads as movement rather than warning or success; and
     muted covers every way a record stops being live. */
  .badge.st-waiting{background:var(--mustard-soft);color:#8A5A1E;}
  .badge.st-progress{background:var(--blue-soft);color:#2E5570;}
  .badge.st-done{background:var(--sage-soft);color:#3E5340;}
  .badge.st-muted{background:var(--subtle-bg);color:var(--text-muted);}
  .badge.customer-created{background:var(--mustard-soft);color:#7A4E13;}
  .badge.sentiment-not{background:var(--brick-soft);color:#8C332E;}
  .badge.sentiment-follow{background:var(--blue-soft);color:#2E5570;}
  .company-tag-pill{position:relative;padding-right:16px;}
  .company-tag-remove{display:none;position:absolute;right:4px;top:50%;transform:translateY(-50%);cursor:pointer;font-weight:600;line-height:1;}
  .company-tag-pill:hover .company-tag-remove{display:inline;}
  .company-tag-pill:hover{padding-right:20px;}
  .badge.sentiment-nurture{background:var(--sage-soft);color:#3E5340;}
  .badge.sentiment-connected{background:var(--green-dot);color:#12281A;}
  .badge.warn{background:var(--brick-soft);color:#8C332E;}
  .badge.neutral{background:var(--subtle-bg);color:var(--text-muted);box-shadow:inset 0 0 0 1px var(--line);}
  .icon-chip{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--ink-soft);}

  .dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:9px;flex-shrink:0;}
  .dot.customer{background:var(--green-dot);}
  .dot.prospect{background:var(--yellow-dot);}
  .dot.lead{background:var(--blue);}
  .dot.inv-paid{background:var(--green-dot);}
  .dot.inv-unpaid{background:var(--yellow-dot);}
  .dot.inv-overdue{background:var(--brick);}
  .dot.inv-voided{background:var(--text-muted);}
  /* Paid but not yet cleared — distinct from both paid and unpaid, because it
     is genuinely neither. */
  .dot.inv-processing{background:var(--sage);}
  .name-with-dot{display:flex;align-items:center;}

  .company-header{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;margin-bottom:8px;flex-wrap:wrap;}
  .company-title-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
  /* The logo carries its own right margin for the inline cases (list rows,
     which aren't flex and would ignore a gap). Here the row's gap already does
     the spacing, so the margin would double it. */
  .company-title-row img, .company-title-row .company-logo{margin-right:0;}
  /* The drawn fallback logo. Muted rather than full ink: it stands in for a
     logo, and at --ink it would read as heavier than the real favicons it
     lines up beside. */
  .company-logo-ph{color:var(--line-strong);}
  .company-address{font-size:13px;color:var(--ink-soft);margin-top:4px;}
  .company-meta-row{font-size:13px;color:var(--ink-soft);margin-top:2px;display:flex;gap:14px;flex-wrap:wrap;}
  .company-meta-row a{color:var(--blue);}
  .section{margin-top:30px;}
  .dashboard-section{margin-top:24px;background:var(--paper-raised);border:1px solid var(--line);border-radius:10px;padding:20px 22px;}
  .dashboard-section:first-child{margin-top:0;}

  .sync-icon{display:inline-block;}
  .sync-icon.spinning{animation:etsy-spin 1s linear infinite;}
  @keyframes etsy-spin{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}

  .price-slider{position:relative;height:26px;margin-top:8px;}
  .price-slider input[type=range]{position:absolute;left:0;top:10px;width:100%;margin:0;pointer-events:none;-webkit-appearance:none;background:transparent;}
  .price-slider input[type=range]#pf-price-min{z-index:2;}
  .price-slider input[type=range]#pf-price-max{z-index:1;}
  .price-slider input[type=range]::-webkit-slider-runnable-track{height:4px;background:transparent;border-radius:2px;}
  .price-slider input[type=range]::-moz-range-track{height:4px;background:transparent;border-radius:2px;}
  .price-slider::before{content:"";position:absolute;left:0;right:0;top:50%;height:4px;transform:translateY(-50%);background:var(--line);border-radius:2px;z-index:0;}
  .price-slider input[type=range]::-webkit-slider-thumb{pointer-events:auto;-webkit-appearance:none;width:15px;height:15px;border-radius:50%;background:var(--ink);border:2px solid var(--paper);cursor:pointer;margin-top:-5.5px;}
  .price-slider input[type=range]::-moz-range-thumb{pointer-events:auto;width:15px;height:15px;border-radius:50%;background:var(--ink);border:2px solid var(--paper);cursor:pointer;}

  .alerts-strip{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:20px;}
  .alert-pill{display:flex;align-items:center;gap:7px;background:var(--paper-raised);border:1px solid var(--line);border-radius:20px;padding:7px 14px;font-size:12px;color:var(--ink-soft);cursor:pointer;}
  .alert-pill:hover{border-color:var(--line-strong);}
  .alert-pill .alert-count{font-family:var(--font-display);font-weight:600;font-size:14px;color:var(--ink);}
  .alert-pill.warn{border-color:var(--brick);}
  .alert-pill.warn .alert-count{color:var(--brick);}
  /* Only what needs attention gets a pill, so a pill can afford to be filled.
     Overdue (past a date) is brick; pending work to schedule is mustard. Text
     comes from the same hue as its fill, the standing rule for tinted
     backgrounds — --ink on a colour reads as a mistake. */
  .alert-pill.overdue{background:var(--brick-soft);border-color:var(--brick-soft);color:var(--brick);}
  .alert-pill.overdue .alert-count{color:var(--brick);}
  .alert-pill.pending{background:var(--mustard-soft);border-color:var(--mustard-soft);color:var(--mustard);}
  .alert-pill.pending .alert-count{color:var(--mustard);}
  /* Stated rather than merely absent: an empty strip reads as a failed load. */
  .alerts-clear{display:flex;align-items:center;font-size:12px;color:var(--text-muted);}
  .section-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;}
  .section-title{font-family:var(--font-display);font-size:18px;font-weight:600;margin:0;}

  .metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-top:16px;}
  .metric-card{background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;padding:14px 16px;}
  .metric-card.clickable{cursor:pointer;}
  .metric-card.clickable:hover{border-color:var(--line-strong);}
  .metric-label{font-size:12px;color:var(--ink-soft);display:flex;justify-content:space-between;align-items:center;gap:6px;}
  .metric-value{font-family:var(--font-display);font-size:22px;font-weight:600;margin-top:2px;}
  /* Ageing bands for the Receivables report. Deliberately NOT .metric-card:
     these sit inside a .panel, and a white bordered box inside a white bordered
     box reads as a mistake. They are also a SEQUENCE (current -> 90+), so they
     want one continuous row with dividers rather than six separate objects.
     minmax(104px,...) keeps all six on one line down to a narrow content
     column; below that they wrap evenly rather than stranding one alone. */
  .ageing-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:0;}
  .ageing-band{padding:2px 14px;border-left:1px solid var(--line);}
  .ageing-band:first-child{padding-left:0;border-left:none;}
  .ageing-band-label{font-size:12px;color:var(--ink-soft);}
  .ageing-band-value{font-family:var(--font-display);font-size:20px;font-weight:600;margin-top:2px;}
  .metric-value.warn{color:var(--brick);}

  .status-history{list-style:none;padding:0;margin:14px 0 0;font-size:13px;color:var(--ink-soft);}
  .status-history li{padding:8px 0;border-bottom:1px solid var(--line);}
  .status-history li:last-child{border-bottom:none;}
  .status-history .entry-time{display:block;font-size:11px;color:var(--text-muted);margin-top:2px;}

  table.data{width:100%;border-collapse:collapse;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;overflow:hidden;font-size:13px;}
  table.data th{text-align:left;font-family:var(--font-mono);font-size:10px;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);font-weight:400;padding:9px 12px;border-bottom:1px solid var(--line);}
  table.data td{padding:9px 12px;border-bottom:1px solid var(--line);}
  table.data tr:last-child td{border-bottom:none;}
  table.data tr:hover td{background:var(--subtle-bg);}

  .order-block{border:1px solid var(--line);border-radius:8px;background:var(--paper-raised);margin-bottom:12px;overflow:hidden;}
  .order-block-head{display:flex;justify-content:space-between;align-items:center;padding:11px 14px;border-bottom:1px solid var(--line);background:var(--subtle-bg);flex-wrap:wrap;gap:8px;}
  .order-block-meta{display:flex;align-items:center;gap:10px;font-size:13px;flex-wrap:wrap;}
  .order-total{font-family:var(--font-mono);font-weight:500;}
  .order-actions{display:flex;gap:6px;flex-wrap:wrap;}

  .line-item-row{display:grid;grid-template-columns:1.4fr 0.35fr 1fr 0.85fr auto;gap:10px;align-items:center;margin-bottom:8px;position:relative;}
  .line-item-row.header{font-family:var(--font-mono);font-size:10px;text-transform:uppercase;color:var(--text-muted);margin-bottom:6px;}
  .line-item-remove{cursor:pointer;color:var(--brick);font-size:12px;border:1px solid var(--brick);border-radius:4px;padding:6px 8px;background:transparent;}
  .ph-type-btn{flex:1;display:flex;align-items:center;justify-content:center;border:none;background:var(--paper-raised);color:var(--ink-soft);cursor:pointer;padding:0;}
  .ph-type-btn:first-child{border-right:1px solid var(--line-strong);}
  .ph-type-btn.active{background:var(--ink);color:var(--paper);}
  .ph-ext:disabled{background:transparent;border-color:transparent;}
  .order-total-line{display:flex;justify-content:flex-end;gap:8px;margin-top:10px;font-size:14px;font-weight:500;}
  /* The results panel is absolutely positioned, so it needs a positioned
     parent — without one it anchors to the page and lands somewhere unrelated. */
  .sku-search-wrap{position:relative;}
  .sku-search-input{width:100%;}
  /* Assigned SKUs as removable chips. Typing an exact code from memory was the
     old way; this shows what is already attached without reading a CSV. */
  .sku-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;}
  .sku-chip{display:inline-flex;align-items:center;gap:6px;font-family:var(--font-mono);font-size:11px;background:var(--sage-soft);color:var(--sage-ink);border-radius:4px;padding:4px 6px 4px 8px;}
  .sku-chip button{border:none;background:transparent;color:inherit;font-size:14px;line-height:1;cursor:pointer;padding:0 2px;opacity:.7;}
  .sku-chip button:hover{opacity:1;}
  .sku-search-results{display:none;position:absolute;top:100%;left:0;right:0;background:var(--paper-raised);border:1px solid var(--line);border-radius:6px;max-height:190px;overflow-y:auto;z-index:60;box-shadow:0 4px 10px rgba(0,0,0,0.1);}
  .sku-search-item{padding:8px 10px;font-size:12px;cursor:pointer;}
  .sku-search-item:hover{background:var(--sage-soft);}

  .filter-menu{position:relative;}
  /* Capped and scrollable: the companies panel carries eleven controls, and an
     uncapped one runs off the bottom of a laptop screen with the Clear button
     on the part nobody can reach. */
  .filter-menu-panel{display:none;position:absolute;top:100%;right:0;margin-top:6px;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;box-shadow:0 4px 10px rgba(0,0,0,0.1);z-index:60;padding:12px;min-width:272px;max-width:300px;max-height:min(70vh,560px);overflow-y:auto;flex-direction:column;gap:2px;}
  /* Grouped rather than one unbroken column of fields: the headings are what
     let somebody find "how many sites" without reading every label. */
  .filter-section{display:flex;flex-direction:column;gap:7px;padding:9px 0;border-bottom:1px solid var(--line);}
  .filter-section:last-of-type{border-bottom:none;}
  .filter-section:first-of-type{padding-top:2px;}
  .filter-section-head{font-size:10px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-soft);opacity:.75;}
  .filter-section .field{margin:0;gap:3px;}
  .filter-section .field label{font-size:11px;}
  .filter-section .sub-text{font-size:11px;line-height:1.35;}
  /* A min/max pair on one line. Two stacked full-width fields for what is
     obviously one range is what made the old panel need scrolling. */
  .filter-range{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:5px;}
  .filter-range-input{width:100%;min-width:0;font-size:12px;padding:5px 7px;}
  .filter-range-sep{font-size:10px;color:var(--ink-soft);opacity:.7;}
  /* Segmented control. Replaces the "Show archived instead" checkbox, which sat
     alone above a column of dropdowns and read as an afterthought. Active and
     Archived are two views of one list, and a two-state control says that where
     a lone checkbox does not. */
  .seg{display:flex;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:var(--paper);}
  .seg button{flex:1 1 0;min-width:0;border:none;background:transparent;color:var(--ink-soft);
    font-size:11px;font-weight:600;padding:5px 6px;cursor:pointer;white-space:nowrap;
    border-right:1px solid var(--line);font-family:inherit;}
  .seg button:last-child{border-right:none;}
  .seg button:hover{background:var(--paper-raised);color:var(--ink);}
  .seg button.active{background:var(--sage-soft);color:var(--sage-ink);}
  .filter-menu-badge{display:inline-block;background:var(--blue);color:#fff;border-radius:10px;font-size:11px;line-height:1;padding:2px 6px;margin-left:4px;}
  .filter-menu-actions{border-top:1px solid var(--line);padding-top:8px;margin-top:2px;}

  .combo{position:relative;}
  .combo-control{position:relative;}
  .combo-input{width:100%;padding-right:56px;}
  .combo-icons{position:absolute;right:8px;top:50%;transform:translateY(-50%);display:flex;gap:4px;color:var(--ink-soft);cursor:pointer;}
  .combo-panel{display:none;position:absolute;top:100%;left:0;right:0;margin-top:4px;background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;box-shadow:0 4px 10px rgba(0,0,0,0.1);z-index:70;max-height:160px;overflow-y:auto;}
  .combo-option{padding:8px 10px;font-size:13px;cursor:pointer;}
  .combo-option:hover{background:var(--sage-soft);}
  .combo-group-label{padding:6px 10px 2px;font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);font-family:var(--font-mono);}
  /* Checkbox filter dropdown: several real filters are "any of several"
     (Nick's or Sara's, overdue or due soon), which a single <select> can only
     ever express one answer to. */
  /* Multi-select filter. The control itself is a real .combo (same input, same
     magnifier and chevron), so it reads as the control people already know from
     every single-select filter; only the panel differs, carrying checkboxes. */
  .fms{position:relative;}
  /* EVERY rule here is scoped under .fms — a bare .fms-option (0,1,0) LOSES to
     the global `.field label` (0,1,1), which sets display:block and a bottom
     margin. That is exactly what broke the first version: the checkbox and its
     text stacked with a gap between every row. */
  .fms .fms-panel{display:none;position:absolute;top:100%;left:0;right:0;margin-top:4px;
    background:var(--paper-raised);border:1px solid var(--line);border-radius:8px;
    box-shadow:0 4px 10px rgba(0,0,0,0.1);z-index:70;max-height:220px;overflow-y:auto;padding:4px;}
  .fms label.fms-option{display:flex;align-items:center;gap:8px;margin:0;padding:6px 8px;
    font-size:12px;font-weight:400;color:var(--ink);cursor:pointer;border-radius:5px;line-height:1.3;}
  .fms label.fms-option:hover{background:var(--sage-soft);}
  .fms label.fms-option input[type="checkbox"]{margin:0;flex:0 0 auto;width:14px;height:14px;
    accent-color:var(--sage-ink);cursor:pointer;}
  .fms .fms-option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .combo-empty{padding:10px;font-size:12px;color:var(--ink-soft);}
  .combo-disabled .combo-input{background:var(--subtle-bg);color:var(--text-muted);cursor:not-allowed;}
  .combo-disabled .combo-icons{cursor:not-allowed;opacity:0.5;}

  .variation-row{border:1px solid var(--line);border-radius:6px;padding:10px 12px;margin-bottom:10px;background:var(--paper);}

  .task-row{display:flex;align-items:center;gap:10px;padding:9px 4px;border-bottom:1px solid var(--line);}
  .task-row:last-child{border-bottom:none;}
  .task-title{font-size:13px;font-weight:500;}
  .task-meta{font-size:11px;color:var(--ink-soft);margin-top:1px;}
  .task-meta.overdue{color:var(--brick);}

  .day-group{margin-bottom:16px;}
  .day-group-head{font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);margin-bottom:6px;}
  .day-group-head.overdue{color:var(--brick);}
  .day-group-head.today{color:var(--mustard);}

  .overlay{position:fixed;inset:0;background:rgba(31,58,52,0.35);display:flex;align-items:flex-start;justify-content:center;padding:40px 16px;overflow-y:auto;z-index:100;}
  .modal{background:var(--paper-raised);border-radius:10px;padding:24px 26px;width:100%;max-width:560px;border:1px solid var(--line);}
  .modal h2{font-family:var(--font-display);font-size:20px;margin:0 0 18px;font-weight:600;}
  .field{margin-bottom:14px;position:relative;}
  .field label{display:block;font-size:12px;color:var(--ink-soft);margin-bottom:5px;font-weight:500;}
  .field input, .field textarea, .field select{width:100%;}
  .field-row{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
  .field-row.three{grid-template-columns:1fr 1fr 1fr;}

  .field-error-msg{position:absolute;top:0;right:0;color:var(--brick);font-size:11px;font-weight:600;margin:0;white-space:nowrap;}
  .field.field-invalid input,
  .field.field-invalid select{border-color:var(--brick) !important;}
  .modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px;}
  .hint{font-size:12px;color:var(--text-muted);margin-top:20px;font-family:var(--font-mono);}
  .pagination{display:flex;justify-content:center;gap:12px;align-items:center;margin-top:14px;padding-bottom:16px;}

  .login-screen{min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--paper);}
  .login-card{background:var(--paper-raised);border:1px solid var(--line);border-radius:10px;padding:36px 34px;width:100%;max-width:360px;box-shadow:0 4px 20px rgba(31,58,52,0.08);}
  .login-card .brand-mark{font-family:var(--font-display);font-size:22px;font-weight:600;color:var(--ink);display:block;margin-bottom:4px;}
  .login-card .login-sub{font-size:13px;color:var(--ink-soft);margin:0 0 24px;}
  .login-card .btn{width:100%;margin-top:6px;}
  .login-error{background:var(--brick-soft);color:#8C332E;font-size:13px;padding:10px 12px;border-radius:var(--radius);margin-bottom:16px;display:none;}
  .login-error.visible{display:block;}

  .typing-dots{display:inline-flex;align-items:center;gap:3px;padding:2px 0;}
  .typing-dots span{width:6px;height:6px;border-radius:50%;background:var(--ink-soft);opacity:0.4;animation:typing-bounce 1.2s infinite ease-in-out;}
  .typing-dots span:nth-child(2){animation-delay:0.2s;}
  .typing-dots span:nth-child(3){animation-delay:0.4s;}
  @keyframes typing-bounce{
    0%, 60%, 100%{transform:translateY(0);opacity:0.4;}
    30%{transform:translateY(-4px);opacity:1;}
  }

  .order-thumb{width:44px;height:44px;flex-shrink:0;border-radius:6px;object-fit:cover;border:1px solid var(--line);margin:2px;}
  .order-thumb-placeholder{display:flex;align-items:center;justify-content:center;position:relative;cursor:pointer;background:var(--subtle-bg);}
  /* The grayscale filter here used to exist only to desaturate a colour emoji.
     The icon is now a currentColor stroke, so there is nothing left to drain. */
  .order-thumb-icon{display:flex;opacity:0.4;}
  .order-thumb-pencil{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.35);border-radius:6px;opacity:0;transition:opacity 0.15s ease;}
  /* The one place an icon is given an explicit colour rather than inheriting.
     It sits on a fixed dark scrim, not on theme paper, so currentColor would
     render it near-invisible on the five light themes. */
  .order-thumb-pencil .ic{color:#fff;}
  .order-thumb-placeholder:hover .order-thumb-pencil{opacity:1;}

/* ---------- GNOMEWORKERS ----------
   Two panes: a channel rail and the message flow. Both read the --panel-*
   variables through .panel, so the five block styles reach them like every
   other content panel. */
.gw-layout{display:grid;grid-template-columns:230px minmax(0,1fr);gap:12px;align-items:stretch;}
/* A fixed height rather than page flow, so the composer stays put and the
   history scrolls under it. Without it the input walks off the bottom of the
   screen the moment a channel has more than a screenful in it. */
.gw-rail,.gw-main{height:calc(100vh - 190px);min-height:340px;overflow:hidden;padding:0;}
.gw-rail{overflow-y:auto;padding:8px 0;}
.gw-rail-group{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);
  padding:10px 12px 4px;font-weight:600;}
.gw-rail-row{display:flex;align-items:center;justify-content:space-between;gap:6px;
  padding:6px 12px;cursor:pointer;font-size:13px;color:var(--ink-soft);}
.gw-rail-row:hover{background:var(--subtle-bg);}
.gw-rail-row.active{background:var(--subtle-bg);color:var(--ink);font-weight:600;}
/* Unread is carried by WEIGHT plus the badge, never by colour alone — the rail
   already spends its colour on the nav group tints. */
.gw-rail-row.unread{color:var(--ink);font-weight:600;}
.gw-rail-name{display:flex;align-items:center;gap:7px;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.gw-rail-name .ic{width:14px;height:14px;flex-shrink:0;opacity:.6;}
.gw-rail-badge{flex-shrink:0;font-family:var(--font-mono);font-size:10px;line-height:1;
  background:var(--brick);color:#fff;border-radius:9px;padding:3px 6px;}

.gw-main{display:flex;flex-direction:column;}
.gw-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;
  padding:12px 16px;border-bottom:1px solid var(--line);flex-shrink:0;}
.gw-head-name{font-family:var(--font-display);font-size:17px;font-weight:600;}
.gw-head-topic{margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.gw-flow{flex:1;overflow-y:auto;padding:12px 16px;}

.gw-msg{position:relative;padding:5px 0;}
/* A run from the same person loses its byline, so a back-and-forth reads as a
   conversation rather than a list of stamped records. */
.gw-msg.grouped{padding-top:1px;}
.gw-msg-head{display:flex;align-items:baseline;gap:8px;margin-bottom:1px;}
.gw-msg-who{font-weight:600;font-size:13px;}
.gw-msg-when{font-size:11px;color:var(--text-muted);font-family:var(--font-mono);}
.gw-msg-body{font-size:13.5px;line-height:1.5;white-space:normal;word-break:break-word;}
.gw-edited{font-size:11px;}
.gw-msg.deleted .gw-msg-body{opacity:.65;}
/* Revealed on hover rather than always drawn: a Delete button beside every line
   makes a conversation look like a table of records to be maintained. */
.gw-msg-actions{position:absolute;top:0;right:0;display:none;gap:4px;
  background:var(--paper-raised);padding:2px;border-radius:6px;}
.gw-msg:hover .gw-msg-actions{display:flex;}

.gw-composer{display:flex;gap:8px;align-items:flex-end;padding:10px 16px;
  border-top:1px solid var(--line);flex-shrink:0;}
.gw-composer textarea{flex:1;resize:none;}

@media (max-width:820px){
  /* The rail becomes a short scrolling strip above the flow. Side by side below
     this width leaves the messages too narrow to read. */
  .gw-layout{grid-template-columns:minmax(0,1fr);}
  .gw-rail{height:auto;max-height:190px;min-height:0;}
  .gw-main{height:calc(100vh - 330px);}
}

/* Slice 2: DMs and private channels in the rail. */
.gw-rail-dm-head{display:flex;align-items:center;justify-content:space-between;gap:6px;margin-top:6px;}
/* A bare + rather than a .btn: at rail width a button chip crowds the heading
   out, and this sits inside a label rather than beside it. */
.gw-rail-add{background:none;border:0;cursor:pointer;color:var(--text-muted);
  font-size:15px;line-height:1;padding:0 2px;font-family:inherit;}
.gw-rail-add:hover{color:var(--ink);}
.gw-rail-empty{padding:2px 12px 8px;font-size:12px;}
/* The lock beside a private channel's name. Sized down from the 17px .ic
   default because it is an annotation on the title, not a title of its own. */
.gw-lock{width:13px;height:13px;opacity:.55;vertical-align:baseline;}
.gw-person-row{grid-template-columns:minmax(0,1fr);cursor:pointer;}
.gw-person-row:hover{background:var(--subtle-bg);}

/* Slice 3: threads, reactions and the mention menu. */
/* The layout grows a third column only while a thread is open. Toggled by a
   class rather than :has(), so it behaves the same in every browser. */
.gw-layout.with-thread{grid-template-columns:200px minmax(0,1fr) minmax(0,0.9fr);}
.gw-thread{height:calc(100vh - 190px);min-height:340px;overflow:hidden;padding:0;
  display:flex;flex-direction:column;}

.gw-reactions{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px;}
.gw-reaction{display:inline-flex;align-items:center;gap:4px;cursor:pointer;
  border:1px solid var(--line);background:var(--paper-raised);border-radius:11px;
  padding:1px 7px;font-size:11px;font-family:var(--font-mono);color:var(--ink-soft);line-height:1.7;}
.gw-reaction:hover{border-color:var(--line-strong);}
/* Yours is outlined, not filled: a filled pill at this size loses the emoji
   against the accent, and the border reads at a glance either way. */
.gw-reaction.mine{border-color:var(--sage);color:var(--ink);}
.gw-react-palette{display:inline-flex;gap:1px;margin-right:4px;}
.gw-react-pick{border:0;background:none;cursor:pointer;font-size:13px;line-height:1;
  padding:2px 3px;border-radius:5px;}
.gw-react-pick:hover{background:var(--subtle-bg);}

/* A link, not a button chip: it sits under a message and a chip there competes
   with the message itself for attention. */
.gw-thread-link{display:inline-block;margin-top:3px;border:0;background:none;padding:0;
  cursor:pointer;font-family:inherit;font-size:12px;color:var(--sage-ink);font-weight:500;}
.gw-thread-link:hover{text-decoration:underline;}

/* Positioned off the textarea and appended to the body, because the composer
   has overflow rules that would clip an inline menu. */
.gw-mention-menu{position:fixed;z-index:900;display:none;background:var(--paper-raised);
  border:1px solid var(--line-strong);border-radius:var(--radius);box-shadow:0 6px 18px rgba(0,0,0,0.18);
  overflow:hidden;max-height:220px;overflow-y:auto;}
.gw-mention-option{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:6px 10px;cursor:pointer;font-size:13px;}
.gw-mention-option.active,.gw-mention-option:hover{background:var(--subtle-bg);}

@media (max-width:1100px){
  /* Below this the three columns leave the thread too narrow to read, so it
     takes the main pane's place instead of sitting beside it. */
  .gw-layout.with-thread{grid-template-columns:200px minmax(0,1fr);}
  .gw-layout.with-thread .gw-main{display:none;}
}

/* Slice 5: image attachments. */
.gw-attach-strip{display:none;gap:8px;flex-wrap:wrap;padding:8px 16px 0;flex-shrink:0;}
.gw-attach{position:relative;width:64px;height:64px;border-radius:6px;overflow:hidden;
  border:1px solid var(--line);}
.gw-attach img{width:100%;height:100%;object-fit:cover;display:block;}
/* On a dark scrim rather than theme paper, so a fixed white is right here for
   the same reason .order-thumb-pencil takes one. */
.gw-attach-x{position:absolute;top:2px;right:2px;border:0;cursor:pointer;line-height:1;
  background:rgba(0,0,0,0.55);color:#fff;border-radius:50%;width:17px;height:17px;font-size:13px;
  display:flex;align-items:center;justify-content:center;padding:0;}
.gw-attach-btn{padding:6px 9px;flex-shrink:0;}

.gw-images{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px;}
.gw-image{display:block;line-height:0;border-radius:8px;overflow:hidden;border:1px solid var(--line);}
/* Capped rather than shown at native size: a phone photograph is 4000px wide
   and would push the whole conversation off the screen. The width/height
   attributes reserve the box so the flow does not reflow as each one loads. */
.gw-image img{max-width:260px;max-height:220px;width:auto;height:auto;display:block;object-fit:cover;}

/* Slice 6: record references. */
/* A button, not an anchor: it navigates within the app rather than to a URL,
   and an <a href="#"> would put a junk fragment in the address bar. Styled to
   read as a link inside a sentence, since that is what it is. */
.gw-ref{border:0;background:var(--sage-soft);color:var(--sage-ink);cursor:pointer;
  font-family:inherit;font-size:inherit;line-height:1.3;padding:1px 6px;border-radius:5px;
  font-weight:500;}
.gw-ref:hover{background:var(--sage);color:var(--paper);}

/* Slice 7: search results. */
.gw-result{cursor:pointer;padding:8px 0;border-bottom:1px solid var(--line);}
.gw-result:hover{background:var(--subtle-bg);}
.gw-result-where{font-size:11px;color:var(--sage-ink);background:var(--sage-soft);
  border-radius:4px;padding:1px 5px;}
/* Fades rather than snapping off, so the eye follows it to the line that was
   clicked instead of hunting for what just changed. */
.gw-highlight{background:var(--mustard-soft);transition:background 1.2s ease;}

/* Slice 8: non-image attachments. */
.gw-files{display:flex;flex-direction:column;gap:4px;margin-top:5px;align-items:flex-start;}
.gw-file{display:inline-flex;align-items:center;gap:7px;max-width:320px;
  border:1px solid var(--line);border-radius:7px;padding:5px 9px;text-decoration:none;
  color:var(--ink);background:var(--paper-raised);font-size:12.5px;}
.gw-file:hover{border-color:var(--line-strong);}
.gw-file .ic{width:15px;height:15px;flex-shrink:0;opacity:.6;}
.gw-file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.gw-file-size{color:var(--text-muted);font-family:var(--font-mono);font-size:11px;flex-shrink:0;}
/* A document in the composer strip gets its extension rather than a thumbnail,
   because there is nothing to show and a grey box says less than "PDF". */
.gw-attach.doc{display:flex;align-items:center;justify-content:center;background:var(--subtle-bg);}
.gw-attach-doc{display:flex;flex-direction:column;align-items:center;gap:2px;
  font-size:9px;font-family:var(--font-mono);color:var(--text-muted);}

/* Slice 9: typing indicator. */
/* A fixed-height slot, so an indicator appearing and disappearing never shifts
   the messages above it under the reader's eye. */
.gw-typing{height:16px;padding:0 16px;font-size:11.5px;color:var(--text-muted);
  font-style:italic;flex-shrink:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}

/* ---------- dropping photos onto the library ----------
   The zone wraps the grid rather than being a separate strip, so the target is
   the whole area somebody is already looking at. It has no border of its own
   until a drag is over it: a permanent dashed box would be chrome shouting
   about a feature most visits never use. */
.photo-dropzone{position:relative;border:2px dashed transparent;border-radius:var(--radius);
  transition:border-color .12s ease, background .12s ease;min-height:120px;}
.photo-dropzone.is-dragging{border-color:var(--sage);background:var(--sage-soft);}
.photo-dropzone-hint{display:none;position:absolute;inset:0;z-index:2;
  align-items:center;justify-content:center;font-family:var(--font-display);
  font-size:18px;color:var(--sage-ink);pointer-events:none;}
.photo-dropzone.is-dragging .photo-dropzone-hint{display:flex;}
/* pointer-events:none on the hint is load-bearing: an overlay that swallowed
   the pointer would fire dragleave the instant it appeared, and the highlight
   would strobe as the cursor crossed its own hint. */

/* ---------- global search ----------
   The panel is absolutely positioned under the box, so .gs-wrap is the
   positioning context. z-index above the page but below modals, which own the
   screen when they are open. */
.gs-wrap{position:relative;margin:0 0 18px;}
.rail-foot .gs-wrap{margin:0;}
.gs-box{display:flex;align-items:center;gap:8px;background:var(--paper-raised);
  border:1px solid var(--line);border-radius:var(--radius);padding:8px 11px;}
.gs-box:focus-within{border-color:var(--line-strong);}
.gs-box .ic{color:var(--text-muted);flex-shrink:0;}
.gs-input{flex:1;border:0;background:transparent;font:inherit;font-size:14px;color:var(--ink);outline:none;min-width:0;}
.gs-clear{border:0;background:transparent;font-size:18px;line-height:1;color:var(--text-muted);cursor:pointer;padding:0 2px;}
.gs-panel{position:absolute;left:0;right:0;top:calc(100% + 4px);z-index:60;}
.gs-results{background:var(--paper-raised);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:0 6px 20px rgba(0,0,0,.10);overflow:hidden;max-height:60vh;overflow-y:auto;}
.gs-hint{margin:0;padding:14px 13px;font-size:13px;color:var(--text-muted);}
.gs-scope-chip{border:1px solid var(--line);background:var(--subtle-bg);border-radius:10px;
  font:inherit;font-family:var(--font-mono);font-size:11px;color:var(--ink-soft);
  padding:1px 7px;margin:2px 1px 0 0;cursor:pointer;}
.gs-scope-chip:hover{border-color:var(--line-strong);}
/* The active scope, stated above the results so the narrowing is never
   invisible: a search that quietly looked in one place would read as a search
   that found little. */
.gs-scopebar{display:flex;align-items:center;gap:8px;padding:7px 13px;
  background:var(--blue-soft);color:var(--ink);font-size:12px;}
.gs-scope-clear{border:0;background:transparent;font:inherit;font-size:12px;
  color:var(--blue);cursor:pointer;text-decoration:underline;padding:0;}
.gs-group + .gs-group{border-top:1px solid var(--line);}
.gs-group-head{display:flex;justify-content:space-between;gap:10px;padding:7px 13px;
  background:var(--subtle-bg);font-size:11px;color:var(--ink-soft);}
/* The navigation path, which is the answer to "how do I find this again".
   Stated once per group rather than repeated on every row. */
.gs-path{color:var(--text-muted);white-space:nowrap;}
.gs-row{display:block;width:100%;text-align:left;border:0;background:transparent;font:inherit;
  color:inherit;cursor:pointer;padding:8px 13px;border-top:1px solid var(--line);}
.gs-row:hover,.gs-row.active{background:var(--subtle-bg);}
.gs-row-title{display:block;font-size:14px;}
.gs-row-sub{display:block;font-size:12px;color:var(--text-muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.gs-more{padding:6px 13px;font-size:11px;color:var(--text-muted);border-top:1px solid var(--line);}
/* In the rail the panel would be clipped by the narrow column, so it opens to
   the right of it at a readable width. */
.rail-foot .gs-panel{left:0;width:340px;right:auto;}

/* ---------- mobile navigation (nothing below changes the desktop rail) ----------
 *
 * Twenty-three tabs in a wrapping strip measured 1123px on an 812px screen, so
 * the menu was taller than the phone and every screen started below it. The
 * bell, the user's name and Log out live in .rail-foot, which is display:none
 * at this width, so a phone could not sign out at all.
 *
 * Both new elements default to display:none, so above 820px they contribute
 * nothing: no layout, no flex participation, no stacking context.
 */
.rail-mobile{display:none;}
.nav-pin{display:none;}

@media (max-width:820px){
  /* A COLUMN again. The row-with-wrapping layout is what produced the wall:
     the brand took the left, and twenty-three tabs wrapped down the right. */
  /* Capped so an expanded group can never fill the screen and leave no content
     visible at all — the menu scrolls within itself past that point. A guard,
     not a layout: nothing reaches it until a group is opened. */
  .rail{flex-direction:column;align-items:stretch;gap:10px;padding:12px 16px;
    max-height:80vh;overflow-y:auto;}
  .brand{flex-shrink:0;}

  /* .tabs is a wrapping ROW at this width, which is what lets eight group
     headings sit on three lines instead of eight. This block is not a tab
     though, so it takes a full-width band of its own at the top rather than
     wrapping in among them — without the basis it landed mid-strip and
     stranded whichever heading wrapped next to it. */
  /* A deliberate TWO-COLUMN GRID rather than a wrap. Wrapping mixed two
     visual languages in one ragged flow — icon-and-label tabs and coloured
     uppercase group headings — so Payouts landed beside CRM and Settings was
     orphaned on a line of its own. A grid gives every entry the same cell and
     the block reads as one thing. */
  .tabs{display:grid;grid-template-columns:1fr 1fr;gap:6px;align-content:start;}
  .rail-mobile{display:flex;flex-direction:column;gap:8px;grid-column:1/-1;order:-1;}

  /* Dashboard is the home of the whole tool and was sitting in the wrap as one
     chip among thirteen. Full width, raised, and first. */
  .tabs > .tab[data-view="dashboard"]{grid-column:1/-1;background:var(--paper-raised);
    border-color:var(--line);font-weight:500;font-size:15px;padding:13px 12px;}
  .tabs > .tab[data-view="dashboard"].active{background:var(--sage-soft);
    border-color:var(--sage);color:var(--sage-ink);}

  /* Every other entry is one cell of equal weight, whether it is a loose tab
     or a group heading. The loose tabs need a surface of their own: beside
     the tinted group headings they read as floating text rather than as
     cells, which is most of what made the block look unfinished. */
  .tabs > .tab, .nav-category-head{min-height:44px;border-radius:var(--radius);}
  .tabs > .tab{background:var(--subtle-bg);border-color:var(--line);}
  /* Settings is the gear in the bar at this width, so the grid cell would be a
     second way to the same place. */
  .tabs > .tab[data-view="settings"]{display:none;}
  .tabs > .tab.active{background:var(--paper-raised);border-color:var(--line-strong);}
  .nav-category.expanded{grid-column:1/-1;}
  .nav-category-body.m-open{margin-top:4px;}

  /* The account row the rail foot cannot show at this width. */
  .rail-m-account{display:flex;align-items:center;gap:10px;
    font-size:12px;color:var(--text-muted);font-family:var(--font-mono);}
  #rail-m-user{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

  /* The toggle that folds the whole menu away. Reads as a control rather than
     a label, because it is the one thing on this bar somebody presses to
     change what they are looking at. */
  .rail-m-menu{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 10px;
    background:var(--paper-raised);border:1px solid var(--line);border-radius:var(--radius);
    font-family:inherit;font-size:12px;color:var(--ink);cursor:pointer;flex:0 0 auto;}
  .rail-m-menu .ic{width:16px;height:16px;}

  /* Collapsed means GONE. Everything folds away except one bar: the toggle,
     who you are, the bell and Log out. The pinned and recent chips go too —
     they are a fast path, but a fast path that is permanently in the way is
     just more chrome, and the whole point of collapsing is to see the tool.
     The brand goes as well: on a phone toolbar you know which app you are in,
     and it was costing a line of its own. */
  .rail.menu-collapsed{max-height:none;overflow:visible;padding:8px 16px;gap:0;}
  .rail.menu-collapsed .brand,
  .rail.menu-collapsed .rail-m-search,
  .rail.menu-collapsed #rail-m-lists,
  .rail.menu-collapsed .tabs > .tab,
  .rail.menu-collapsed .nav-category{display:none;}
  .rail-m-account-actions{display:flex;align-items:center;gap:6px;flex:0 0 auto;}
  /* Sized as real targets. The bell was 25px and Log out 15px tall, sitting
     6px apart — small enough to mis-tap, and one of them signs you out. */
  .rail-m-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;
    min-width:36px;height:36px;background:none;border:0;padding:0;cursor:pointer;
    color:var(--ink-soft);line-height:0;}
  .rail-m-badge{position:absolute;top:-2px;right:-4px;min-width:14px;height:14px;padding:0 3px;
    border-radius:7px;background:var(--brick);color:#fff;font-size:9px;line-height:14px;text-align:center;}
  .rail-m-logout{display:inline-flex;align-items:center;height:36px;padding:0 6px;
    color:var(--ink-soft);text-decoration:none;}

  .rail-m-search{width:100%;font-family:inherit;font-size:14px;padding:9px 11px;
    border:1px solid var(--line);border-radius:var(--radius);
    background:var(--paper-raised);color:var(--ink);}
  .rail-m-search:focus{outline:none;border-color:var(--line-strong);}

  /* ONE "Quick access" block, built from the SAME cell as the menu grid below
     it. As chips this was a third visual language on a panel that already has
     a full-width Dashboard and a two-column grid, so the panel read as three
     stacked ideas rather than one. Two headings became one for the same
     reason: pinned and recent are a single list of shortcuts, told apart by
     whether the star is lit. */
  .rail-m-quick-group{display:flex;flex-direction:column;gap:5px;}
  .rail-m-group-head{font-family:var(--font-mono);font-size:10px;letter-spacing:0.08em;
    text-transform:uppercase;color:var(--text-muted);}
  .rail-m-quick{display:grid;grid-template-columns:1fr 1fr;gap:6px;}
  .rail-m-quick-cell{display:flex;align-items:center;justify-content:space-between;gap:8px;
    min-height:44px;padding:9px 10px 9px 12px;border:1px solid var(--line);
    border-radius:var(--radius);background:var(--paper-raised);
    font-size:13px;color:var(--ink-soft);cursor:pointer;}
  .rail-m-quick-cell.active{border-color:var(--line-strong);color:var(--ink);font-weight:500;}
  .rail-m-quick-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  /* The star is a target of its own inside the cell, so pinning and navigating
     never compete for the same pixels. */
  .rail-m-quick-pin{display:inline-flex;align-items:center;justify-content:center;
    flex:0 0 auto;min-width:28px;height:28px;margin:-4px -4px -4px 0;
    color:var(--line-strong);line-height:0;}
  .rail-m-quick-pin.pinned{color:var(--mustard);}
  .rail-m-quick-pin .ic{width:14px;height:14px;}

  /* Desktop's open state is neutralised and the mobile one drives the accordion.
     Source order matters here: .m-open must win when both classes are present. */
  .nav-category-body.open{display:none;}
  .nav-category-body.m-open{display:flex;flex-direction:column;flex-wrap:nowrap;}

  /* Filtering hides with a class, never with style.display, which
     applyNavVisibility() owns — the two writing the same property would let a
     cleared search reveal a tab somebody is not allowed to see. */
  .tab.nav-filtered-out{display:none !important;}
  .nav-category.nav-group-filtered-out{display:none !important;}

  /* Every row a comfortable target, and the pin far enough from the label that
     one is not mistaken for the other. */
  .tab{padding:11px 12px;}
  .nav-category-head{padding:10px 12px;}
  .nav-pin{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
    background:none;border:0;padding:4px;margin:-4px -4px -4px 0;cursor:pointer;
    color:var(--line-strong);line-height:0;}
  .nav-pin .ic{width:14px;height:14px;}
  .nav-pin.pinned{color:var(--mustard);}
  /* No pin on a LOOSE top-level tab. Dashboard, Reports, Finance, Payouts and
     Settings are already one tap away with nothing to expand, so a shortcut to
     them saves nothing and five extra stars make the strip read as busy. */
  .tabs > .tab .nav-pin{display:none;}
  /* The count sits between the label and the pin and must not be pushed out. */
  .tab .count{flex:0 0 auto;}
}

/* ---------- multi-step to dos ---------- */

.td-multi-toggle{display:flex;align-items:center;gap:8px;font-size:14px;cursor:pointer;margin:2px 0 10px;}
.td-multi-toggle input{width:16px;height:16px;}

.td-steps{display:flex;flex-direction:column;gap:6px;}
.td-step{display:flex;align-items:center;gap:6px;}
.td-step input[type=text]{flex:1;min-width:0;}
/* A completed step is a record of something that happened, so it cannot be
   renamed, moved or removed from the form. The server refuses all three; the
   disabled controls mean the refusal is never reached. */
.td-step.done input[type=text]{color:var(--text-muted);text-decoration:line-through;}
.td-step-n{font-family:var(--font-mono);font-size:11px;color:var(--text-muted);width:14px;flex:0 0 auto;text-align:right;}
.td-step .btn.small{flex:0 0 auto;padding:6px 8px;line-height:1;}

/* ONE fixed slot for whichever marker a row carries. The ring is 32px and a
   checkbox 17px, so without a shared slot a multi-step row's title sat 15px
   right of a single-step one and the list read as ragged. The same
   fixed-width-column lesson the Inventory and Manage Users grids already
   carry: measure the left edge per row, do not eyeball it. */
.td-mark{flex:0 0 auto;width:32px;margin-right:10px;display:inline-flex;
  align-items:center;justify-content:center;}
.td-mark input[type=checkbox]{width:17px;height:17px;margin:0;}
.task-row .td-mark{cursor:pointer;}
.todo-ring{display:block;}

/* The track in the progress dialog: what is done, what is next, what is left. */
.td-step-track{display:flex;flex-direction:column;border:1px solid var(--line);
  border-radius:var(--radius);overflow:hidden;margin-top:12px;}
.td-track-row{display:flex;gap:10px;padding:9px 12px;border-bottom:1px solid var(--line);font-size:13px;}
.td-track-row:last-child{border-bottom:none;}
.td-track-row.current{background:var(--sage-soft);}
.td-track-row.done .td-track-body > span:first-child{color:var(--text-muted);text-decoration:line-through;}
.td-track-mark{flex:0 0 auto;color:var(--text-muted);line-height:1.4;}
.td-track-row.done .td-track-mark{color:var(--sage);}
.td-track-row.current .td-track-mark{color:var(--mustard);}
.td-track-body{display:flex;flex-direction:column;gap:1px;min-width:0;}

/* ---------- multi-step work orders ---------- */

.wo-steps-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-top:14px;}
.wo-template-pick{display:flex;align-items:center;gap:6px;}
.wo-template-pick .combo{min-width:220px;}

.wo-steps{display:flex;flex-direction:column;gap:8px;}
.wo-step{border:1px solid var(--line);border-radius:var(--radius);padding:8px 10px;
  background:var(--paper-raised);}
/* A completed step has already drawn material down, so nothing about it can be
   edited: the server refuses, and the disabled controls mean the refusal is
   never reached. Reopening is what puts the material back. */
.wo-step.done{background:var(--subtle-bg);}
.wo-step.done input,.wo-step.done select{color:var(--text-muted);}
.wo-step-head{display:flex;align-items:center;gap:6px;}
.wo-step-head input[type=text]{flex:1;min-width:0;}
.wo-step-head .btn.small{flex:0 0 auto;padding:6px 8px;line-height:1;}

.wo-step-materials{display:flex;flex-direction:column;gap:4px;margin:6px 0 0 22px;}
.wo-step-material{display:flex;align-items:center;gap:6px;}
.wo-step-material select{flex:1 1 auto;min-width:0;}
.wo-step-material input[type=number]{width:96px;flex:0 0 auto;}
.wo-step-material .btn.small{flex:0 0 auto;padding:6px 8px;line-height:1;}

.wo-ring{display:inline-flex;cursor:pointer;flex:0 0 auto;}

/* ---------- work order step timers ---------- */

/* The clock button lights up once a step carries a duration, so a timed step
   is visible without opening its panel. */
.btn.small.wo-timed{color:var(--mustard);border-color:var(--mustard);}

.wo-step-timer{display:flex;align-items:flex-end;gap:8px;flex-wrap:wrap;
  margin:6px 0 0 22px;padding:8px 10px;border:1px solid var(--mustard);
  border-radius:var(--radius);background:var(--mustard-soft);}
.wo-step-timer .micro-text{color:#8A5A1E;}
.wo-timer-unit{display:flex;flex-direction:column;align-items:center;gap:2px;}
.wo-timer-unit input{width:62px;text-align:center;}
.wo-step-timer-summary{margin:4px 0 0 22px;}

/* The countdown itself. Mono, because a figure that changes every second
   should not make the text beside it jump as the digits change width. */
.wo-timer{display:inline-flex;align-items:center;gap:5px;font-family:var(--font-mono);
  font-size:12px;color:#8A5A1E;background:var(--mustard-soft);
  border-radius:20px;padding:2px 9px;white-space:nowrap;}
.wo-timer .ic{width:13px;height:13px;}
.wo-timer.small{font-size:11px;padding:1px 7px;}
/* Ready is a different claim from waiting, so it changes family rather than
   just shade: sage is the app's "done, go ahead" colour. */
.wo-timer.wo-timer-ready{background:var(--sage-soft);color:#3E5340;}
span.wo-timer-ready{color:#3E5340;font-weight:500;}

html[data-scheme="dark"] .wo-timer{color:#E8C98A;}
html[data-scheme="dark"] .wo-timer.wo-timer-ready{color:#BFE0C6;}
html[data-scheme="dark"] .wo-step-timer .micro-text{color:#E8C98A;}

/* Gnome Books: money columns in a report.
   Right-aligned and monospaced, so the decimal points line up down the
   column. This is the one job --font-mono is doing that a proportional face
   would break, which is why a font set never changes it. */
.tb-num{text-align:right;font-family:var(--font-mono);font-size:12px;}
