/* ═══════════════════════════════════════════════════════════════════════
   WAND — SRS & CPF DASHBOARD · stylesheet
   Design tokens first, then the base layout, then the Apple HIG
   normalisation layer (1 Typography … 7 Section F) that pins the type
   scale, the colour usage, the surfaces and the section palette.
   ═══════════════════════════════════════════════════════════════════════ */
    /* ══ DESIGN TOKENS ══ */
    :root {
      /* ── Apple HIG semantic palette (light appearance) ──
         Every surface, label and separator is named the way the system names it, so no
         component invents its own grey: type the same token, get the same look. */
      --bg: #f5f5f7;                    /* systemGroupedBackground — Apple's page grey */
      --surface: #ffffff;               /* cards and sheets */
      --surface2: #f0f0f3;              /* quiet wells, secondary fills */
      --surface3: rgba(0, 0, 0, .028);   /* the faint band that marks a hovered row */

      /* The two hairlines, and only two. A table draws one horizontal rule and nothing
         else; a card draws one outline. Both are barely there — the layout separates
         content, not the ink. */
      --separator: rgba(0, 0, 0, .05);  /* the faint line under a table row */
      --border: rgba(0, 0, 0, .06);     /* the outline around a card */
      --border2: rgba(0, 0, 0, .12);    /* the next step up: a field edge */

      /* Labels: primary / secondary / tertiary. */
      --text: #1d1d1f;
      --text2: #86868b;
      --text3: #a1a1a6;

      /* Tint: system blue, with its hover and pressed steps. */
      --accent: #0071e3;
      --accent-strong: #0077ed;
      --accent-active: #006edb;
      --accent-bg: #e8f2ff;

      /* Status colours come as a pair: a soft, solid bed and a dark ink that sits on it
         without ever shouting. A pastel badge reads as a state, not as an alarm. */
      --green: #34c759;
      --green-ink: #2e7d32;
      --green-bg: #e8f5e9;
      --red: #ff3b30;
      --red-ink: #c62828;
      --red-bg: #fdecea;
      --amber: #ff9500;
      --amber-ink: #b26a00;
      --amber-bg: #fff4e5;

      /* The segmented-control track: the system's own translucent fill. */
      --track: rgba(118, 118, 128, .12);

      /* ── Materials ──
         Translucent chrome that sits over content. The blur lives in --blur, so every
         material blurs by the same amount and the depth reads as one system. */
      --material: rgba(255, 255, 255, .75);
      --material-thick: rgba(255, 255, 255, .88);
      --material-hairline: rgba(255, 255, 255, .55);
      --blur: saturate(180%) blur(20px);
      --scroll-overlay: rgba(0, 0, 0, .28);

      /* ── Section palette ──
         One accent per section, in the spirit of Apple's product line-up: a vivid
         colour that identifies the section, plus a very light tint for its card. */
      --sec-blue: #0a84ff;
      --sec-blue-tint: rgba(10, 132, 255, .055);
      --sec-teal: #00c2c7;
      --sec-teal-tint: rgba(0, 194, 199, .06);
      --sec-indigo: #5e5ce6;
      --sec-indigo-tint: rgba(94, 92, 230, .055);
      --sec-green: #30d158;
      --sec-green-tint: rgba(48, 209, 88, .07);
      --sec-orange: #ff9f0a;
      --sec-orange-tint: rgba(255, 159, 10, .07);
      --sec-purple: #bf5af2;
      --sec-purple-tint: rgba(191, 90, 242, .06);
      --sec-pink: #ff375f;
      --sec-pink-tint: rgba(255, 55, 95, .055);
      --sec-slate: #6a6a70;
      --sec-slate-tint: rgba(106, 106, 112, .05);

      /* ── Corner radii ──
         Generous, the way an iOS widget and a macOS panel are drawn: a field, an inset
         tile, a card and a sheet step up 10 → 14 → 20 → 26, and the capsule is a full
         pill. */
      --radius-xs: 8px;
      --radius-sm: 10px;
      --radius-md: 14px;
      --radius-lg: 20px;
      --radius-xl: 26px;
      --radius-pill: 999px;

      /* ── Elevation ──
         Almost no shadow at all: one wide, very low-opacity diffusion that reads as
         ambient light rather than as a drop shadow, and nothing darker than 4% at rest. */
      --shadow-sm: 0 4px 24px rgba(0, 0, 0, .04);
      --shadow-md: 0 6px 32px rgba(0, 0, 0, .07);
      --shadow-lg: 0 16px 56px rgba(0, 0, 0, .12);

      /* ── Type ──
         SF first on Apple platforms; the web font is only the fallback for everything
         else, and the CJK faces come before the generic. */
      --font-sans:
        -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Helvetica Neue",
        Inter, Arial, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
      --font-mono: "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      --fs-hero: 32px;
      --fs-hero-sm: 28px;
      --fs-value: 20px;
      --fs-value-sm: 18px;
      --fs-card-title: 14px;
      --fs-label: 12px;
      --fs-sub: 12px;
      --ls-label: .05em;

      /* ── Motion ──
         The tab pill glides on Apple's own easing curve: a fast start, a soft landing.
         --ease-apple is the interaction curve and --ease-spring the one that overshoots
         slightly, which is what makes a press feel physical. */
      --ease-tab: cubic-bezier(0.32, 0.72, 0, 1);
      --ease-apple: cubic-bezier(0.25, 0.1, 0.25, 1);
      --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
      --transition: all .22s var(--ease-spring);
      --ease-out: all .2s ease-out;
      --dur-fast: .15s;
      --dur: .25s;
      --dur-slow: .4s;

      /* The system's focus ring: a soft blue halo, never a hard outline. */
      --focus-ring: 0 0 0 4px rgba(0, 113, 227, .22);
    }

    /* ── Dark appearance ──
       The same semantic tokens, read the way the system reads them at night: a near-black page,
       graphite cards, labels that step down from white, and accents lifted one stop so they keep
       their contrast on the dark bed. Nothing here invents a component colour — every dark rule
       below only re-points a token the light design already uses. */
    html[data-theme="dark"] {
      color-scheme: dark;

      --bg: #0b0b0d;                    /* systemBackground — the page at night */
      --surface: #17171a;               /* cards and sheets */
      --surface2: #232327;              /* quiet wells, secondary fills */
      --surface3: rgba(255, 255, 255, .055);

      --separator: rgba(255, 255, 255, .08);
      --border: rgba(255, 255, 255, .10);
      --border2: rgba(255, 255, 255, .20);

      --text: #f5f5f7;
      --text2: #a1a1a6;
      --text3: #83838a;

      --accent: #0a84ff;
      --accent-strong: #409cff;
      --accent-active: #0070e0;
      --accent-bg: rgba(10, 132, 255, .18);

      --green: #30d158;
      --green-ink: #5ee78a;
      --green-bg: rgba(48, 209, 88, .15);
      --red: #ff453a;
      --red-ink: #ff7b72;
      --red-bg: rgba(255, 69, 58, .16);
      --amber: #ff9f0a;
      --amber-ink: #ffc061;
      --amber-bg: rgba(255, 159, 10, .16);

      --track: rgba(118, 118, 128, .26);

      --material: rgba(24, 24, 27, .70);
      --material-thick: rgba(24, 24, 27, .88);
      --material-hairline: rgba(255, 255, 255, .10);
      --scroll-overlay: rgba(255, 255, 255, .26);

      --sec-blue: #0a84ff;
      --sec-blue-tint: rgba(10, 132, 255, .12);
      --sec-teal: #40d1d5;
      --sec-teal-tint: rgba(0, 194, 199, .12);
      --sec-indigo: #7d7bf5;
      --sec-indigo-tint: rgba(94, 92, 230, .14);
      --sec-green: #30d158;
      --sec-green-tint: rgba(48, 209, 88, .12);
      --sec-orange: #ffa733;
      --sec-orange-tint: rgba(255, 159, 10, .12);
      --sec-purple: #cf7bf7;
      --sec-purple-tint: rgba(191, 90, 242, .13);
      --sec-pink: #ff5c7c;
      --sec-pink-tint: rgba(255, 55, 95, .12);
      --sec-slate: #98989d;
      --sec-slate-tint: rgba(152, 152, 157, .08);

      --shadow-sm: 0 4px 24px rgba(0, 0, 0, .35);
      --shadow-md: 0 6px 32px rgba(0, 0, 0, .45);
      --shadow-lg: 0 16px 56px rgba(0, 0, 0, .60);

      --focus-ring: 0 0 0 4px rgba(10, 132, 255, .34);
    }

    /* ══ BASE RESET ══ */
    *,
    *::before,
    *::after {
      box-sizing: border-box;
      margin: 0;
      padding: 0
    }

    html {
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      -webkit-text-size-adjust: 100%;
      text-size-adjust: 100%;
      background: var(--bg);
      scroll-behavior: smooth
    }

    body {
      min-height: 100vh;
      font-family: var(--font-sans);
      color: var(--text);
      background: var(--bg);
      text-rendering: optimizeLegibility;
      -webkit-tap-highlight-color: transparent
    }

    /* The system's own selection colour, at the system's own opacity. */
    ::selection {
      background: rgba(0, 113, 227, .22);
      color: var(--text)
    }

    /* ══ APP SHELL: header, tabs ══ */
    .app-wrapper {
      margin: 0 auto
    }

    .app-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 12px
    }

    .header-left {
      display: flex;
      align-items: center;
      gap: 14px
    }

    .header-right {
      display: flex;
      align-items: center;
      gap: 8px
    }

    /* ── Colour mode ──
       A two-stop segmented control in the header, drawn on the same track as the dashboard
       tabs: the chosen mode takes the raised pill, the other stays quiet. The labels fold
       away on a phone so the switch keeps the width of two icons. */
    .theme-switch {
      display: inline-flex;
      align-items: center;
      gap: 2px;
      padding: 3px;
      background: var(--track);
      border-radius: var(--radius-pill)
    }

    .theme-option {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 6px 12px;
      border: 0;
      border-radius: var(--radius-pill);
      background: transparent;
      color: var(--text2);
      font: inherit;
      font-size: var(--fs-sub);
      font-weight: 500;
      line-height: 1;
      cursor: pointer;
      transition: background var(--dur-fast) ease-out, color var(--dur-fast) ease-out,
        box-shadow var(--dur-fast) ease-out
    }

    .theme-option svg {
      width: 14px;
      height: 14px;
      flex: 0 0 auto
    }

    .theme-option:hover {
      color: var(--text)
    }

    .theme-option.active {
      background: var(--surface);
      color: var(--text);
      box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 0 0 .5px rgba(0, 0, 0, .04)
    }

    .theme-option:focus-visible {
      outline: none;
      box-shadow: var(--focus-ring)
    }

    .app-logo {
      width: 44px;
      height: 44px;
      display: flex;
      align-items: center;
      justify-content: center
    }

    .app-logo svg {
      width: 24px;
      height: 24px;
      fill: none;
      stroke: #fff;
      stroke-width: 2
    }

    .app-name {
      font-weight: 700
    }

    .app-sub {
      font-size: 13px;
      color: var(--text2);
      margin-top: 1px
    }

    .section-intro {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 16px
    }

    .section-date {
      font-size: 12px;
      color: var(--text3)
    }

    .tab-panel {
      display: none
    }

    .tab-panel.active {
      display: block
    }

    /* The Tracker and Task tabs embed standalone pages (tracker.html and task.html) in
       iframes. Each fills the content column and a tall slice of the viewport, scrolling
       internally so the dashboard page itself does not grow. The warm fill matches those
       pages' own --bg, so the frame never flashes a different colour while the document loads. */
    .tracker-frame,
    .task-frame {
      display: block;
      width: 100%;
      height: calc(100vh - 210px);
      min-height: 640px;
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      background: #FBF7F2;
      box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 8px 24px rgba(150, 110, 80, .10)
    }

    .tab-empty {
      min-height: 360px;
      display: grid;
      place-items: center;
      text-align: center;
      color: var(--text2);
      background: rgba(255, 255, 255, .55);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      padding: 48px
    }

    .tab-empty strong {
      display: block;
      color: var(--text);
      font-size: 18px;
      margin-bottom: 8px
    }

    .investment-table-wrap {
      overflow-x: auto;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-sm)
    }

    .investment-table {
      width: 100%;
      min-width: 620px;
      border-collapse: collapse;
      font-size: 13px
    }

    .investment-table th,
    .investment-table td {
      padding: 16px 18px;
      border-bottom: 1px solid var(--separator);
      text-align: left
    }

    .investment-table th {
      color: var(--text2);
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .05em;
      text-transform: uppercase
    }

    .investment-table th.num,
    .investment-table td.num {
      text-align: right;
      font-variant-numeric: tabular-nums
    }

    .investment-table tr:last-child td {
      border-bottom: 0
    }

    .investment-name {
      color: var(--text);
      font-weight: 600
    }

    .investment-note {
      color: var(--text3);
      font-size: 12px;
      margin-top: 3px
    }

    .market-link {
      color: var(--accent);
      text-decoration: none
    }

    .market-link:hover {
      text-decoration: underline
    }

    .market-link-icon {
      display: inline-block;
      margin-left: 4px;
      font-size: 11px;
      color: var(--text3)
    }

    .section-account-meta {
      display: flex;
      flex-direction: column;
      gap: 3px;
      text-align: right;
      font-size: 12px;
      color: var(--text3);
      line-height: 1.5
    }

    .section-account-meta strong {
      color: var(--text2);
      font-weight: 500
    }

    /* Import and its file list are the same job seen from two angles, so they share one line instead
       of stacking into a column of full-width buttons that pushed the account details down. The meta
       takes its own width instead of shrinking to half the heading row, which is what forced the two
       buttons onto separate lines in the first place. */

    .section-account-meta--actions {
      flex-shrink: 0
    }

    .section-meta-row {
      display: flex;
      align-items: stretch;
      justify-content: flex-end;
      flex-wrap: nowrap;
      gap: 6px;
      margin-bottom: 2px
    }

    .section-meta-row .btn {
      padding: 8px 15px;
      font-size: 13px
    }

    .cash-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 16px;
      margin-bottom: 20px
    }

    .cash-grid.four {
      grid-template-columns: repeat(4, minmax(0, 1fr))
    }

    /* ══ CASH & PROPERTY CARDS ══ */
    .cash-item {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 16px 18px;
      box-shadow: var(--shadow-sm);
      cursor: pointer;
      transition: var(--ease-out)
    }

    .cash-item:hover {
      border-color: var(--border2);
      box-shadow: var(--shadow-md);
      transform: translateY(-2px)
    }

    /* Read-only cards (for example Cash Receivable, which mirrors the Revenue tab). */
    .cash-item.is-readonly,
    .cash-item.is-readonly:hover {
      cursor: default;
      transform: none
    }

    .cash-item-label {
      color: var(--text2);
      font-size: 13px;
      font-weight: 600;
      margin-bottom: 10px
    }

    .cash-item-value {
      font-size: 24px;
      font-weight: 700;
      letter-spacing: -.5px
    }

    .cash-edit-input {
      width: 100%;
      border: 1px solid var(--accent);
      border-radius: var(--radius-sm);
      padding: 4px 6px;
      background: var(--surface);
      color: var(--text);
      font: inherit;
      outline: none
    }

    .cash-total {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 14px 20px;
      margin: -4px 0 20px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
      box-shadow: var(--shadow-sm)
    }

    .cash-total-label {
      color: var(--text2);
      font-size: 13px;
      font-weight: 600
    }

    .cash-total-value {
      font-size: 20px;
      font-weight: 700
    }

    .section-total {
      background: transparent;
      border: 0;
      border-top: 1px solid var(--border);
      border-radius: 0;
      box-shadow: none;
      padding: 14px 4px 4px;
      margin: 0 0 8px
    }

    .section-total .cash-total-value {
      font-size: 18px
    }
    .cash-total.is-clickable {
      cursor: pointer;
      transition: var(--ease-out)
    }

    .cash-total.is-clickable:hover {
      border-color: var(--border2);
      box-shadow: var(--shadow-md);
      transform: translateY(-2px)
    }

    .property-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 16px;
      margin-bottom: 20px
    }

    .property-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 20px;
      box-shadow: var(--shadow-sm);
      cursor: pointer;
      transition: var(--ease-out)
    }

    .property-card:hover {
      border-color: var(--border2);
      box-shadow: var(--shadow-md);
      transform: translateY(-2px)
    }

    .property-card-title {
      font-size: 15px;
      font-weight: 650;
      margin-bottom: 14px
    }

    .property-card-row {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      gap: 3px;
      padding: 8px 0;
      border-top: 1px solid var(--border);
      color: var(--text2);
      font-size: 13px
    }

    .property-card-row strong {
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .property-card-note {
      color: var(--text3);
      font-size: 11px;
      margin-top: 12px
    }

    .property-total-grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 12px;
      margin-bottom: 20px
    }

    .property-total-grid .cash-total {
      margin: 0;
      display: block
    }

    .property-total-grid .cash-total-value {
      display: block;
      margin-top: 6px
    }

    .net-worth-grid {
      display: grid
    }

    .net-worth-grid .net-worth-result {
      border-color: rgba(58, 107, 191, .25)
    }

    .property-detail-groups {
      display: flex;
      flex-direction: column;
      gap: 16px
    }

    .property-detail-group {
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      overflow: hidden;
      background: var(--surface)
    }

    .property-detail-group-title {
      padding: 8px 14px;
      background: var(--surface2);
      border-bottom: 1px solid var(--border2);
      color: var(--text2);
      font-size: 10px;
      font-weight: 700;
      letter-spacing: .09em;
      text-transform: uppercase
    }

    .property-detail-list {
      display: flex;
      flex-direction: column
    }

    .property-detail-field {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 18px;
      padding: 9px 14px;
      border-bottom: 1px solid var(--border);
      font-size: 13px
    }

    .property-detail-field:last-child {
      border-bottom: 0
    }

    .property-detail-field span {
      flex: 1 1 auto;
      min-width: 0;
      color: var(--text2);
      line-height: 1.35
    }

    .property-detail-field span::after {
      content: ":"
    }

    .property-detail-value {
      flex: 0 0 auto;
      color: var(--text);
      font-weight: 600;
      text-align: right;
      white-space: nowrap
    }

    .property-edit-input {
      width: 150px;
      border: 1px solid var(--accent);
      border-radius: var(--radius-sm);
      padding: 5px 7px;
      background: var(--surface);
      color: var(--text);
      font: inherit;
      text-align: right
    }

    @media(max-width:650px) {
      .property-grid,
      .property-total-grid,
      .net-worth-grid {
        grid-template-columns: 1fr
      }

      .property-detail-field {
        gap: 12px;
        padding: 9px 12px
      }

      .property-edit-input {
        max-width: 100%
      }
    }

    .cash-item-note {
      color: var(--text3);
      font-size: 11px;
      margin-top: 6px
    }

    /* ── Networth edit marks ──
       A pencil marks a figure that can be changed in place; an "auto" chip marks
       a total that is calculated from other figures. Both are drawn as generated
       content, so the repeated textContent updates in app.js cannot wipe them. */
    .nw-editable-value,
    .investment-table td.investment-editable {
      cursor: pointer
    }

    .investment-table td.investment-editable {
      transition: background .15s ease
    }

    .investment-table td.investment-editable:hover {
      background: var(--surface3)
    }

    .nw-mark-pencil,
    .nw-editable-value::after,
    .property-detail-value.nw-editable-value::after,
    .investment-table td.investment-editable::after {
      color: var(--accent);
      font-size: 11px;
      font-weight: 500;
      line-height: 1;
      vertical-align: 2px;
      pointer-events: none;
      transition: opacity .15s ease
    }

    .nw-editable-value::after,
    .property-detail-value.nw-editable-value::after,
    .investment-table td.investment-editable::after {
      content: "✎";
      margin-left: 5px;
      opacity: .55
    }

    .nw-editable-value:hover::after,
    .property-detail-value.nw-editable-value:hover::after,
    .investment-table td.investment-editable:hover::after {
      opacity: 1
    }

    /* The pencil steps aside while the inline editor is open. */
    .nw-editable-value:focus-within::after,
    .property-detail-value.nw-editable-value:focus-within::after,
    .investment-table td.investment-editable:focus-within::after {
      content: none
    }

    .nw-mark-auto,
    .nw-auto-value::after {
      display: inline-block;
      margin-left: 6px;
      padding: 1px 5px;
      border: 1px solid var(--border);
      border-radius: 999px;
      background: var(--surface2);
      color: var(--text2);
      font-size: 9px;
      font-weight: 600;
      line-height: 1.35;
      letter-spacing: .04em;
      white-space: nowrap;
      vertical-align: middle;
      pointer-events: none
    }

    .nw-auto-value::after {
      content: "auto"
    }

    /* The three headline totals keep their chip in the card corner so the larger
       hero figures and their labels do not have to compete for the same line. */
    .net-worth-grid .cash-total {
      position: relative
    }

    .net-worth-grid .nw-auto-value::after {
      position: absolute;
      top: 12px;
      right: 14px;
      margin-left: 0
    }

    .net-worth-grid .net-worth-result .nw-auto-value::after {
      top: 18px;
      right: 20px
    }

    .nw-mark-legend {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 8px 18px;
      margin: -20px 0 6px;
      padding: 0 4px;
      color: var(--text3);
      font-size: 11px;
      font-weight: 500
    }

    .nw-mark-legend-item {
      display: inline-flex;
      align-items: center;
      gap: 6px
    }

    @media(max-width:650px) {
      .nw-mark-legend {
        gap: 6px 14px;
        font-size: 10.5px
      }
    }

    @media(max-width:800px) {
      .cash-grid.four {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }
    }

    @media(max-width:520px) {
      .cash-grid,
      .cash-grid.four {
        grid-template-columns: 1fr
      }
    }

    /* ══ CARDS (SRS, CPF) ══ */
    .cards-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 16px;
      margin-bottom: 20px
    }

    @media(max-width:600px) {
      .cards-grid {
        grid-template-columns: 1fr
      }
    }

    .card {
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      border: 1px solid var(--border);
      cursor: pointer;
      position: relative;
      overflow: hidden;
      transition: var(--transition);
      user-select: none
    }

    .card:hover {
      transform: translateY(-3px);
      box-shadow: var(--shadow-md);
      border-color: var(--border2)
    }

    .card:active {
      transform: translateY(-1px)
    }

    .card-tag {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .04em;
      text-transform: uppercase;
      padding: 4px 10px;
      margin-bottom: 14px
    }

    .card-tag svg {
      width: 12px;
      height: 12px
    }

    .card-value {
      font-size: 32px;
      font-weight: 700;
      letter-spacing: -1px;
      line-height: 1.1;
      margin-bottom: 6px
    }

    .card-sub {
      font-size: 13px;
      color: var(--text2);
      display: flex;
      align-items: center;
      gap: 6px;
      flex-wrap: wrap
    }

    .card-pill {
      display: inline-flex;
      align-items: center;
      gap: 3px;
      font-size: 12px;
      font-weight: 600;
      padding: 2px 8px;
      border-radius: 20px
    }

    .card-pill.up {
      background: var(--green-bg);
      color: var(--green)
    }
    .card-arrow {
      position: absolute;
      top: 20px;
      right: 20px;
      width: 28px;
      height: 28px;
      border-radius: 50%;
      background: var(--bg);
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--text3);
      transition: var(--ease-out)
    }

    .card:hover .card-arrow {
      background: var(--border2);
      color: var(--text)
    }

    .card-arrow svg {
      width: 14px;
      height: 14px
    }

    .metrics-bar {
      background: var(--surface);
      border-radius: var(--radius-md);
      padding: 20px 24px;
      box-shadow: var(--shadow-sm);
      border: 1px solid var(--border);
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      margin-bottom: 20px
    }

    @media(max-width:520px) {
      .metrics-bar {
        grid-template-columns: 1fr
      }
    }

    .metric-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: 0 20px
    }

    .metric-item.is-clickable {
      cursor: pointer;
      border-radius: var(--radius-sm);
      transition: var(--ease-out)
    }

    .metric-item.is-clickable:hover {
      background: var(--bg)
    }

    .metric-item:not(:last-child) {
      border-right: 1px solid var(--border)
    }

    .metric-label {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .05em;
      text-transform: uppercase;
      color: var(--text3);
      margin-bottom: 4px
    }

    .metric-value {
      font-size: 26px;
      font-weight: 700;
      letter-spacing: -.6px
    }

    .metric-badge {
      font-size: 11px;
      font-weight: 500;
      color: var(--text3);
      margin-top: 1px
    }

    .status-banner {
      background: var(--surface);
      border-radius: var(--radius-md);
      padding: 16px 20px;
      box-shadow: var(--shadow-sm);
      border: 1px solid var(--border);
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
      flex-wrap: wrap
    }

    .status-left {
      display: flex;
      align-items: center;
      gap: 12px
    }

    .status-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: var(--green);
      box-shadow: 0 0 0 3px var(--green-bg);
      flex-shrink: 0
    }

    .status-text {
      font-size: 14px;
      color: var(--text2)
    }

    .status-text strong {
      color: var(--text);
      font-weight: 600
    }

    .status-contribution {
      font-size: 13px;
      color: var(--text2);
      background: var(--amber-bg);
      border-radius: var(--radius-xs);
      padding: 6px 12px;
      white-space: nowrap
    }

    .status-contribution strong {
      color: var(--amber);
      font-weight: 600
    }

    .modal-backdrop {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, .45);
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
      z-index: 100;
      opacity: 0;
      pointer-events: none;
      transition: opacity .2s ease
    }

    .modal-backdrop.open {
      opacity: 1;
      pointer-events: all
    }

    /* ══ MODALS ══ */
    .modal {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      width: min(744px, 100vw);
      background: var(--surface);
      z-index: 101;
      display: flex;
      flex-direction: column;
      transform: translateX(100%);
      transition: transform .3s cubic-bezier(0.32, 0.72, 0, 1);
      box-shadow: var(--shadow-lg)
    }

    .modal.open {
      transform: translateX(0)
    }

    .modal-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 24px 24px 20px;
      border-bottom: 1px solid var(--border);
      flex-shrink: 0
    }

    .modal-title {
      font-size: 18px;
      font-weight: 700;
      letter-spacing: -.3px
    }

    .modal-close {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      border: none;
      background: var(--bg);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--text2);
      transition: var(--ease-out)
    }

    .modal-close:hover {
      background: var(--border2);
      color: var(--text)
    }

    .modal-close svg {
      width: 16px;
      height: 16px
    }

    .modal-body {
      flex: 1;
      overflow-y: auto;
      padding: 24px;
      display: flex;
      flex-direction: column;
      gap: 20px
    }

    .modal-body::-webkit-scrollbar {
      width: 6px
    }

    .modal-body::-webkit-scrollbar-track {
      background: transparent
    }

    .modal-body::-webkit-scrollbar-thumb {
      background: var(--border2);
      border-radius: 3px
    }

    /* Breakdown tables can be wider than a phone; keep the note and header fixed
       while the table itself pans sideways. */
    .modal-table-scroll {
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      min-width: 0
    }

    .modal-table-scroll::-webkit-scrollbar {
      height: 10px
    }

    .modal-table-scroll::-webkit-scrollbar-track {
      background: transparent
    }

    .modal-table-scroll::-webkit-scrollbar-thumb {
      background: var(--border2);
      border-radius: 5px
    }

    /* Every table spans its card exactly, so a tinted row reaches the right edge and the
       action buttons sit in a fixed-width column instead of drifting. */
    .data-table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13.5px
    }

    .data-table td,
    .data-table th {
      box-sizing: border-box
    }

    /* The row-action column (▲ ▼ ×) keeps a fixed width, and the header above it matches,
       so the tinted row and the header end at the same place. */
    .data-table .rev-row-actions,
    .data-table .rev-actions-head {
      width: 92px;
      min-width: 92px;
      white-space: nowrap;
      text-align: right;
      padding-right: 10px
    }

    /* ══ DATA TABLES ══ */
    .data-table th {
      text-align: left;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .05em;
      text-transform: uppercase;
      padding: 12px 14px;
      border-bottom: 1px solid var(--separator);
      white-space: nowrap
    }

    .data-table td {
      padding: 16px 14px;
      border-bottom: 1px solid var(--separator);
      vertical-align: middle
    }

    .data-table tr:last-child td {
      border-bottom: none
    }

    .data-table tr:hover td {
      background: var(--bg)
    }

    .data-table td.num {
      text-align: right;
      font-variant-numeric: tabular-nums
    }

    /* The breakdown sheets use the same pencil / auto vocabulary as the tab: the
       source loan is editable, everything derived from it is calculated. */
    .data-table td.nw-auto-value,
    .data-table td.nw-editable-value {
      white-space: nowrap
    }

    .data-table td.nw-auto-value::after {
      margin-left: 4px;
      padding: 1px 4px;
      font-size: 8.5px
    }

    .data-table td.nw-editable-value {
      cursor: pointer
    }

    .data-table td.nw-editable-value::after {
      margin-left: 4px
    }

    .data-table td.nw-editable-value:hover {
      background: var(--surface3)
    }

    .data-table th.num {
      text-align: right
    }

    .data-table td.mono {
      font-family: 'SF Mono', 'Fira Code', monospace;
      font-size: 12px
    }

    /* ══ CHIPS, BADGES & BUTTONS ══ */
    .chip {
      display: inline-block;
      font-size: 11px;
      font-weight: 500;
      padding: 2px 8px;
      border-radius: var(--radius-pill);
      white-space: nowrap
    }

    /* One soft pastel bed and one dark ink per asset class, so a chip reads as a quiet
       label rather than as a highlight. */
    .chip.reit {
      background: #e8f2ff;
      color: #0a5fc0
    }

    .chip.bank {
      background: #e4f6f7;
      color: #0a6b74
    }

    .chip.etf {
      background: #e8f5e9;
      color: #2e7d32
    }

    .chip.bond {
      background: #fff4e5;
      color: #b26a00
    }

    .chip.tbill {
      background: #f6ecfd;
      color: #7a2ea8
    }

    .val-up {
      color: var(--green-ink);
      font-weight: 500
    }

    .val-down {
      color: var(--red);
      font-weight: 500
    }

    .summary-card {
      background: var(--bg);
      border-radius: var(--radius-md);
      padding: 18px;
      display: flex;
      justify-content: space-between;
      align-items: center
    }
    .summary-card-value {
      font-size: 20px;
      font-weight: 700;
      letter-spacing: -.4px
    }

    .sub-section-title {
      font-size: 13px;
      font-weight: 600;
      color: var(--text2);
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 2px
    }

    .sub-section-title::after {
      content: '';
      flex: 1;
      height: 1px;
      background: var(--border)
    }

    /* ══ CHARTS (donuts, legends, tooltips) ══ */
    .donut-wrap {
      display: flex;
      align-items: center;
      gap: 32px;
      flex-wrap: wrap
    }

    /* Every donut is drawn at 140×140 and scales up from there, so the ring is a real chart rather
       than a thumbnail — and it keeps scaling with the window instead of being pinned at one size.
       The plain width first is the fallback for a browser that cannot read the clamp; where the
       clamp is understood it wins. A ring never drops below 150px (still readable) and never grows
       past 260px (so the legend stays beside it rather than dropping to its own line). */
    .donut-wrap svg {
      width: 190px;
      width: clamp(150px, 14vw, 260px);
      height: auto;
      flex: 0 0 auto
    }

    .donut-legend {
      display: flex;
      flex-direction: column;
      gap: 10px
    }

    .legend-item {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 13px
    }

    .legend-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      flex-shrink: 0
    }

    /* Net worth and revenue donuts share one hover treatment (see .rev-mix-slice and
       .rev-mix-legend-row further down), so the two chart areas read as one set. */

    /* ── Revenue mix donut: the ring and its legend point at each other ──
       Hovering a slice (or its legend row) thickens that ring, dims the others and
       highlights the matching legend row, so it is obvious which slice is which. */
    .rev-mix-slice {
      cursor: pointer;
      transition: stroke-width .22s ease-out, opacity .22s ease-out
    }

    .rev-mix-slice.is-active {
      stroke-width: 27
    }

    .rev-mix-slice.is-dimmed {
      opacity: .3
    }

    .rev-mix-legend-row {
      padding: 6px 10px;
      margin: 0 -10px;
      border-radius: 10px;
      transition: background .22s ease-out, opacity .22s ease-out, transform .22s ease-out
    }

    .rev-mix-legend-row.is-active {
      background: var(--surface2);
      box-shadow: inset 3px 0 0 var(--row-accent, var(--accent));
      transform: translateX(2px)
    }

    .rev-mix-legend-row.is-dimmed {
      opacity: .45
    }

    /* One shared floating tooltip, used by the donut wedges and the CPF growth points. The script
       places it from the pointer's viewport coordinates, so the card is kept at the root of the
       document rather than inside a chart: sections in a wide-screen grid carry container-type,
       which rests on layout containment, and layout containment is what makes an element the
       containing block for its fixed children (see section 8, and the note in index.html). */
    .chart-tooltip {
      position: fixed;
      z-index: 80;
      pointer-events: none;
      opacity: 0;
      visibility: hidden;
      transition: opacity .12s ease-out;
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: var(--radius-sm);
      box-shadow: var(--shadow-md, 0 6px 20px rgba(0, 0, 0, .12));
      padding: 8px 10px;
      font-size: 12px;
      line-height: 1.45;
      max-width: 280px
    }

    .chart-tooltip.show {
      opacity: 1;
      visibility: visible
    }

    .chart-tooltip-title {
      font-weight: 600;
      color: var(--text)
    }

    .chart-tooltip-meta {
      color: var(--text3)
    }

    .chart-wrap {
      background: var(--bg);
      border-radius: var(--radius-md);
      padding: 16px;
      overflow: hidden
    }

    .chart-title {
      font-size: 12px;
      font-weight: 600;
      color: var(--text2);
      margin-bottom: 12px
    }

    /* Charts side by side (for example the gross and liability-adjusted net worth donuts). */
    .chart-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 16px
    }

    /* The Production tab's mix strip carries three rings — the active streams, Section C's passive
       income and the two added together. The tracks are sized to the card, not the window: as many
       ~340px columns as fit, then two, then one, so a ring is never squeezed and the strip still
       uses all the width a Studio Display gives it. The card can be half of a wide grid, which is
       why the track width decides rather than a viewport breakpoint. */
    .chart-grid--three {
      grid-template-columns: repeat(auto-fit, minmax(340px, 1fr))
    }

    @media(max-width:800px) {
      .chart-grid,
      .chart-grid--three {
        grid-template-columns: 1fr
      }
    }

    #growthChart {
      width: 100%;
      height: 180px;
      display: block
    }

    .year-badge {
      display: inline-block;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: var(--radius-xs);
      padding: 2px 8px;
      font-size: 12px;
      font-weight: 600;
      color: var(--text2)
    }

    .contrib-row {
      display: flex;
      align-items: center;
      gap: 12px
    }

    .contrib-year {
      font-size: 14px;
      font-weight: 600;
      min-width: 48px
    }

    .contrib-bar-wrap {
      flex: 1
    }

    .contrib-bar-track {
      height: 8px;
      background: var(--border);
      border-radius: 99px;
      overflow: hidden
    }

    .contrib-bar-fill {
      height: 100%;
      border-radius: 99px;
      background: #0071e3
    }

    .contrib-amount {
      font-size: 13px;
      font-weight: 600;
      min-width: 100px;
      text-align: right
    }

    .contrib-note {
      font-size: 11px;
      color: var(--text3);
      margin-top: 2px
    }

    .info-note {
      font-size: 12px;
      color: var(--text3);
      background: var(--bg);
      border-radius: var(--radius-sm);
      padding: 10px 12px;
      border-left: 3px solid var(--border2)
    }

    /* ── The handover note modal ──
       A read-only line saying which tenancy the note is for, then the note itself in a monospace
       box: the "1) 2) 3)" list has to line up, and an edit made for one tenant should read as a
       deliberate change rather than disappearing into the prose. */
    .handover-note-meta {
      font-size: 12px;
      line-height: 1.45;
      color: var(--text2);
      background: var(--bg);
      border-radius: var(--radius-sm);
      padding: 9px 12px;
      border-left: 3px solid var(--accent);
      margin-bottom: 12px
    }

    .handover-note-text {
      display: block;
      width: 100%;
      box-sizing: border-box;
      height: min(56vh, 520px);
      min-height: 220px;
      padding: 14px 16px;
      border: 1px solid var(--border2);
      border-radius: var(--radius-sm);
      background: var(--surface);
      color: var(--text);
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 12px;
      line-height: 1.65;
      white-space: pre-wrap;
      resize: vertical
    }

    .handover-note-text:focus {
      outline: none;
      border-color: var(--accent);
      box-shadow: 0 0 0 3px var(--accent-bg)
    }

    /* `.btn` sets its own display, which outbids the browser's [hidden] rule, so the send button
       says so itself when the tenancy records no number. */
    #handover-note-wa[hidden] {
      display: none
    }

    /* The note is as long as it is, so the sheet does not need any special body sizing: the note,
       then the three buttons, then the rest of the sheet. */

    .edit-tabs {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin-bottom: 4px
    }

    .edit-tab {
      padding: 8px 16px;
      cursor: pointer;
      transition: var(--ease-out);
      font-family: inherit
    }
    .edit-panel {
      display: none;
      flex-direction: column;
      gap: 16px
    }

    .edit-panel.active {
      display: flex
    }

    .form-group {
      display: flex;
      flex-direction: column;
      gap: 5px
    }

    .form-label {
      font-size: 12px;
      font-weight: 500;
      color: var(--text2);
      letter-spacing: .02em
    }

    .form-input,
    .form-input:focus {
      border-color: var(--accent);
      box-shadow: 0 0 0 3px rgba(58, 107, 191, .12)
    }

    .form-row {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 12px
    }

    @media(max-width:400px) {
      .form-row {
        grid-template-columns: 1fr
      }
    }

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      border: none;
      cursor: pointer;
      font-family: inherit;
      /* A button drawn on an anchor ("Open folder", "Send on WhatsApp") is still a button: the
         browser's underline belongs to links in prose, not to a capsule. */
      text-decoration: none;
      transition: var(--ease-out)
    }

    .btn-danger:hover {
      background: #f9e0dd
    }
    .btn-ghost:hover {
      color: var(--text)
    }

    .btn-row {
      display: flex;
      gap: 8px;
      flex-wrap: wrap
    }

    .edit-table-wrap {
      overflow-x: auto;
      /* The widest tables (Section A.2) have more columns than fit, and the columns on the right
         used to be cut off with nothing to say they were there. A styled, always-on horizontal
         scrollbar keeps the hidden columns discoverable, and dragging the table itself pans it
         too (see initTableScrollDrag).
         The bar is drawn with ::-webkit-scrollbar further down. scrollbar-width / scrollbar-color
         are deliberately NOT set here: when either is present Chromium ignores ::-webkit-scrollbar
         entirely and falls back to a thin macOS overlay bar that auto-hides, which is exactly the
         "there is no scrollbar" symptom this is meant to cure. Firefox, which has no
         ::-webkit-scrollbar, gets the standard properties through the @supports fallback. */
      overscroll-behavior-x: contain
    }

    /* Only a table that actually overflows gets the grab cursor (set from JS), so a narrow table
       that has nothing to pan still reads as plain. */
    .edit-table-wrap.is-pannable {
      cursor: grab
    }

    /* A real drag pans the table; the cursor says so while it is happening. */
    .edit-table-wrap.is-dragging,
    .edit-table-wrap.is-dragging * {
      cursor: grabbing
    }

    body.table-dragging,
    body.table-dragging * {
      user-select: none
    }

    /* An always-on horizontal bar, tall and dark enough to see and grab at a glance. Styling
       ::-webkit-scrollbar is what stops macOS from hiding an overlay bar until it is used. */
    .edit-table-wrap::-webkit-scrollbar {
      height: 12px
    }

    .edit-table-wrap::-webkit-scrollbar-track {
      background: var(--surface2);
      border-radius: 999px;
      margin: 0 2px
    }

    .edit-table-wrap::-webkit-scrollbar-thumb {
      background: var(--text3);
      border: 3px solid var(--surface2);
      border-radius: 999px;
      min-width: 44px
    }

    .edit-table-wrap::-webkit-scrollbar-thumb:hover {
      background: var(--accent)
    }

    /* Firefox and any engine without ::-webkit-scrollbar: the strongest visible bar on offer. */
    @supports not selector(::-webkit-scrollbar) {
      .edit-table-wrap {
        scrollbar-width: thin;
        scrollbar-color: var(--text3) var(--surface2)
      }
    }

    .edit-cell-input {
      width: 100%;
      border: 1px solid transparent;
      background: transparent;
      padding: 4px 6px;
      border-radius: 5px;
      font-family: inherit;
      font-size: 13px;
      outline: none;
      transition: border-color .15s
    }

    .edit-cell-input:focus {
      border-color: var(--accent);
      background: var(--surface)
    }

    /* ── Editing a table cell, Excel style ──
       The field fills the cell exactly: no border, no extra padding, so opening an editor never
       changes the row's height or the column's width. A thin inset ring (a box-shadow, which
       takes no space) marks the cell being edited. */
    .data-table .cash-edit-input {
      width: 100%;
      border: 0;
      border-radius: 2px;
      background: var(--surface);
      color: var(--text);
      font: inherit;
      padding: 0 4px;
      outline: none;
      box-shadow: inset 0 0 0 2px var(--border2)
    }

    /* The number spinners eat the little width a table cell has; typing works without them. */
    .data-table .cash-edit-input::-webkit-outer-spin-button,
    .data-table .cash-edit-input::-webkit-inner-spin-button {
      -webkit-appearance: none;
      margin: 0
    }

    .data-table .cash-edit-input[type="number"] {
      appearance: textfield;
      -moz-appearance: textfield
    }

    .delete-row-btn {
      width: 24px;
      height: 24px;
      border: none;
      background: transparent;
      color: var(--text3);
      cursor: pointer;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 16px;
      line-height: 1;
      transition: var(--ease-out)
    }

    .delete-row-btn:hover {
      background: var(--red-bg);
      color: var(--red)
    }

    /* Revenue workbook: draggable rows and move controls */
    /* ══ REVENUE TABLES: drag, dropdowns, grouped prospects ══ */
    .rev-drag-cell {
      width: 46px;
      white-space: nowrap;
      color: var(--text3);
      font-variant-numeric: tabular-nums
    }

    /* ── The year a searched row came from ──
       While a search is on the table draws several years' rows at once, so each one carries the year
       it was saved under beside its place in the list — the small tag is what says which year's case
       each row is. The column widens to hold it; with no search the cell is the narrow one it has
       always been. The strip above the table says "every year" or "2023 only" in the same reading. */
    .rev-drag-cell.has-year {
      width: auto;
      padding-right: 10px
    }

    .rev-year-tag {
      display: inline-block;
      margin-left: 6px;
      padding: 1px 6px;
      border-radius: 999px;
      background: var(--surface2);
      color: var(--text2);
      font-size: 10.5px;
      font-weight: 600;
      letter-spacing: .02em;
      vertical-align: 1px
    }

    .rev-year-search-note {
      margin-left: 8px;
      color: var(--text3);
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .02em;
      text-transform: uppercase;
      white-space: nowrap
    }

    /* The totals row's own note: while a search is on it adds up the years on screen, so the row
       says "years shown" beside its label. It reads as a quiet aside, never as part of the total. */
    .rev-total-scope {
      font-weight: 500;
      font-size: 11px;
      letter-spacing: .02em;
      text-transform: uppercase;
      color: var(--text3)
    }

    .rev-drag-handle {
      display: inline-block;
      margin-right: 6px;
      cursor: grab;
      color: var(--text3);
      letter-spacing: -1px
    }

    .rev-row-draggable.dragging {
      opacity: .4
    }

    .rev-row-draggable.drag-insert-above td {
      box-shadow: inset 0 2px 0 var(--accent)
    }

    .rev-row-draggable.drag-insert-below td,
    .rev-total-row.drag-insert-below td {
      box-shadow: inset 0 -2px 0 var(--accent)
    }

    .rev-row-actions {
      white-space: nowrap;
      text-align: right
    }

    .row-move-btn {
      width: 22px;
      height: 22px;
      padding: 0;
      border: none;
      background: transparent;
      color: var(--text3);
      cursor: pointer;
      border-radius: 4px;
      font-size: 10px;
      line-height: 1;
      transition: var(--ease-out)
    }

    .row-move-btn:hover {
      background: var(--bg);
      color: var(--text)
    }

    #toast {
      position: fixed;
      bottom: 32px;
      left: 50%;
      transform: translateX(-50%) translateY(80px);
      background: #1d1d1f;
      color: #fff;
      border-radius: 10px;
      padding: 12px 20px;
      font-size: 14px;
      font-weight: 500;
      box-shadow: var(--shadow-lg);
      z-index: 999;
      transition: transform .3s cubic-bezier(0.34, 1.56, 0.64, 1);
      white-space: nowrap
    }

    #toast.show {
      transform: translateX(-50%) translateY(0)
    }

    #edit-fab {
      position: fixed;
      bottom: 32px;
      right: 32px;
      width: 56px;
      height: 56px;
      border-radius: 50%;
      background: #0071e3;
      color: #fff;
      border: none;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 20px rgba(58, 107, 191, .45);
      font-size: 22px;
      transition: transform .2s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow .2s;
      z-index: 50
    }

    #edit-fab:hover {
      transform: scale(1.1);
      box-shadow: 0 6px 28px rgba(58, 107, 191, .55)
    }

    @keyframes fadeUp {
      from {
        opacity: 0;
        transform: translateY(16px)
      }

      to {
        opacity: 1;
        transform: translateY(0)
      }
    }

    .card:nth-child(1) {
      animation: fadeUp var(--dur-slow) .05s both
    }

    .card:nth-child(2) {
      animation: fadeUp var(--dur-slow) .12s both
    }

    .card:nth-child(3) {
      animation: fadeUp var(--dur-slow) .19s both
    }

    .card:nth-child(4) {
      animation: fadeUp var(--dur-slow) .26s both
    }

    .metrics-bar {
      animation: fadeUp var(--dur-slow) .33s both
    }

    .status-banner {
      animation: fadeUp var(--dur-slow) .40s both
    }

    .section-block {
      padding-top: 32px;
      border-top: 1px solid var(--separator)
    }

    .section-heading {
      margin-bottom: 4px
    }

    .section-heading-sub {
      font-size: 13px;
      color: var(--text2);
      margin-bottom: 16px
    }

    .card-dual {
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      gap: 12px;
      margin-top: 14px;
      padding-top: 12px;
      border-top: 1px solid var(--border);
      font-size: 13px;
      color: var(--text2)
    }

    .card-dual strong {
      color: var(--text);
      font-weight: 600;
      font-variant-numeric: tabular-nums
    }

    .cpf-legend {
      display: flex;
      gap: 16px;
      flex-wrap: wrap;
      margin-top: 12px;
      font-size: 12px;
      color: var(--text2)
    }

    .used-callout {
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      padding: 14px 16px;
      margin-bottom: 16px
    }

    .used-callout .label {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .06em;
      text-transform: uppercase;
      color: var(--text3);
      margin-bottom: 6px
    }

    .housing-current-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px 18px;
      margin-top: 10px
    }

    .housing-current-row {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      padding: 8px 0;
      border-bottom: 1px solid var(--border);
      font-size: 12px
    }

    .housing-current-row span {
      color: var(--text2)
    }

    .housing-current-row strong {
      font-variant-numeric: tabular-nums
    }

    @media(max-width:520px) {
      .housing-current-grid {
        grid-template-columns: 1fr
      }
    }

    /* ══ CPF GROWTH CHART ══ */
    .cpf-growth-svg {
      width: 100%;
      height: auto;
      display: block
    }

    .cpf-growth-svg .cpf-point {
      cursor: crosshair
    }

    /* ══ SRS GROWTH CHART ══
       Same drawing as the CPF chart, so the two read as one family: a full-width SVG that
       keeps its own aspect ratio, and a legend line of colour dots under it. */

    .growth-chart-svg {
      width: 100%;
      height: auto;
      display: block
    }

    .growth-chart-svg .growth-point {
      cursor: crosshair
    }

    .growth-legend {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
      gap: 6px 14px;
      margin-top: 12px;
      font-size: 11px;
      color: var(--text2)
    }

    .growth-legend .legend-item {
      font-size: 11px
    }

    /* The three lines Section D used to stack in its heading — what can be imported, the
       account and its as-of date — close the card as one quiet footnote. */
    .section-notes {
      display: flex;
      flex-wrap: wrap;
      align-items: baseline;
      gap: 4px 14px;
      margin-top: 14px;
      padding-top: 12px;
      border-top: 1px solid var(--border);
      font-size: 11px;
      color: var(--text3)
    }

    .section-notes > span + span::before {
      content: '·';
      margin-right: 12px;
      color: var(--border2)
    }

    .section-notes strong {
      font-weight: 600;
      color: var(--text2);
      font-variant-numeric: tabular-nums
    }

    @media(max-width:600px) {
      .section-notes {
        flex-direction: column;
        gap: 6px
      }

      .section-notes > span + span::before {
        content: none
      }
    }

    /* A modal header that carries a button as well as its close cross. */
    .modal-header-actions {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-shrink: 0
    }

    /* Design tokens live in the base :root at the top of this stylesheet (Apple HIG palette + type scale). */

    html {
      background: var(--bg)
    }

    body {
      font-family: var(--font-sans);
      background: var(--bg);
      color: var(--text)
    }

    .app-wrapper {
      max-width: 1200px;
      padding: 32px 24px 88px
    }

    .app-header {
      margin-bottom: 18px
    }

    .app-logo {
      background: var(--text);
      border-radius: 14px;
      box-shadow: none
    }

    .app-name {
      font-size: 24px;
      letter-spacing: -.5px
    }

    .app-sub,
    .section-heading-sub,
    .section-date,
    .section-account-meta {
      color: var(--text2)
    }

    .net-worth-grid {
      grid-template-columns: 1fr 1fr 1.35fr;
      gap: 16px;
      margin: 0 0 34px;
      padding: 22px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 20px;
      box-shadow: var(--shadow-sm)
    }

    .net-worth-grid .cash-total {
      margin: 0;
      padding: 12px 14px;
      border: 0;
      box-shadow: none;
      background: transparent
    }

    .net-worth-grid .cash-total-label {
      font-size: 12px;
      color: var(--text2)
    }

    .net-worth-grid .cash-total-value {
      display: block;
      margin-top: 8px;
      font-size: 22px;
      letter-spacing: -.5px;
      font-variant-numeric: tabular-nums
    }

    .net-worth-grid .net-worth-result {
      padding: 18px 20px;
      background: #f0f7ff;
      border: 1px solid rgba(0, 122, 255, .12)
    }

    .net-worth-grid .net-worth-result .cash-total-value {
      font-size: 36px;
      font-weight: 700;
      letter-spacing: -.02em;
      color: var(--text)
    }

    /* The three headline figures need about 1140px to share one row without squeezing the numbers,
       so on anything narrower they stack, each one the full width. The dashboard and the
       stand-alone networth.html both get this, since they render the same panel. */
    @media(max-width:1140px) {
      .net-worth-grid {
        grid-template-columns: 1fr
      }
    }

    .section-block {
      margin-top: 32px;
      padding: 22px;
      border: 1px solid var(--border);
      border-radius: 20px;
      background: rgba(255, 255, 255, .72);
      box-shadow: var(--shadow-sm)
    }

    .section-heading {
      font-size: 12px;
      font-weight: 600;
      letter-spacing: .05em;
      text-transform: uppercase;
      color: var(--text2)
    }
    .cash-grid,
    .property-grid {
      gap: 16px
    }

    .cash-item,
    .property-card,
    .card,
    .summary-card,
    .chart-wrap,
    .used-callout {
      border-radius: 16px;
      border-color: var(--border);
      box-shadow: var(--shadow-sm)
    }

    .cash-item,
    .property-card {
      padding: 18px
    }

    .cash-item-label,
    .property-card-row,
    .summary-card-label {
      color: var(--text2)
    }

    .cash-item-value,
    .property-card-row strong,
    .card-value {
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .card {
      padding: 24px;
      background: var(--surface)
    }

    .card-tag {
      border-radius: 999px
    }

    .btn,
    .edit-tab {
      border-radius: 999px;
      background: #f2f2f7;
      color: var(--accent);
      padding: 8px 16px;
      font-size: 13px;
      font-weight: 500
    }

    .btn-primary,
    .edit-tab.active {
      background: var(--accent);
      color: #fff
    }
    .data-table th {
      color: var(--text2)
    }

    .data-table td {
      border-color: var(--separator)
    }

    .section-total {
      border-top-color: var(--border2)
    }

    @media(max-width:700px) {
      .app-wrapper {
        padding: 22px 16px 64px
      }

      .net-worth-grid {
        grid-template-columns: 1fr;
        padding: 16px
      }

      .net-worth-grid .net-worth-result .cash-total-value {
        font-size: 32px
      }

      .section-block {
        padding: 18px;
        border-radius: 16px
      }
    }

    /* ── Retirement projection ───────────────────────────────────── */
    .retire-milestones {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
      gap: 12px;
      margin-bottom: 18px
    }

    .retire-milestone {
      background: var(--bg);
      border-radius: 12px;
      padding: 12px 14px
    }

    .retire-milestone-label {
      font-size: 11px;
      color: var(--text2)
    }

    .retire-milestone-value {
      font-size: 17px;
      font-weight: 700;
      letter-spacing: -.3px;
      margin: 2px 0
    }

    .retire-milestone-note {
      font-size: 11px;
      color: var(--text3);
      line-height: 1.45
    }

    .retire-assumptions {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(206px, 1fr));
      gap: 10px 12px
    }

    .retire-assumption {
      background: var(--bg);
      border-radius: 12px;
      padding: 9px 11px
    }

    .retire-assumption span {
      display: block;
      font-size: 11px;
      color: var(--text2);
      margin-bottom: 4px;
      line-height: 1.35
    }

    .retire-assumption input,
    .retire-assumption select {
      width: 100%;
      border: 1px solid var(--border);
      background: var(--surface);
      border-radius: 8px;
      padding: 5px 8px;
      font-family: inherit;
      font-size: 13px;
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    /* ══ RETIREMENT PROJECTION ══ */
    .retire-chart-svg {
      width: 100%;
      height: auto;
      display: block
    }

    .retire-legend {
      display: flex;
      gap: 14px;
      flex-wrap: wrap;
      margin-top: 10px
    }

    .retire-row-key td {
      background: var(--accent-bg);
      font-weight: 600
    }

    .retire-tag {
      font-size: 11px;
      color: var(--text3)
    }

    @media(max-width:700px) {
      .retire-assumptions {
        grid-template-columns: 1fr
      }
    }
    /* ════════════════════════════════════════════════════════════════════
       APPLE HIG NORMALISATION LAYER
       Single place that pins the type scale, colour usage and surface look
       so every section reads the same. These rules intentionally win.
       ════════════════════════════════════════════════════════════════════ */

    body {
      font-family: var(--font-sans);
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-rendering: optimizeLegibility
    }

    /* ── 1. Typography ── */

    /* Hero primary values (Total Assets / Net Worth / headline cards). */
    .net-worth-grid .cash-total-value {
      font-size: var(--fs-hero-sm);
      font-weight: 700;
      letter-spacing: -.02em;
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    @media(min-width:900px) {

      .net-worth-grid .cash-total-value {
        font-size: var(--fs-hero)
      }
    }

    /* Section titles → uppercase micro-labels. */
    .section-heading,
    .sub-section-title {
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: var(--ls-label);
      text-transform: uppercase;
      color: var(--text2);
      margin-bottom: 6px
    }

    /* Card titles / account names. */
    .cash-item-label,
    .chart-title,
    .property-detail-field span,
    .cpf-legend .legend-item {
      font-size: var(--fs-card-title);
      font-weight: 500;
      color: var(--text)
    }

    /* Card values. */
    .cash-item-value,
    .card-value,
    .card-dual strong {
      font-size: var(--fs-value);
      font-weight: 600;
      letter-spacing: -.01em;
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .cash-item-value,
    .cash-total-value {
      font-size: var(--fs-value-sm);
      font-weight: 600;
      letter-spacing: -.01em;
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    /* Subtext and descriptions. */
    .cash-item-note,
    .card-sub,
    .card-dual span,
    .section-heading-sub,
    .info-note,
    .app-sub,
    .section-date,
    .legend-item,
    .donut-legend .legend-item div div:last-child {
      font-size: var(--fs-sub);
      color: var(--text2)
    }

    /* Every financial figure lines up: tabular numerals everywhere numbers live. */
    .num,
    .cash-total-value,
    .cash-item-value,
    .card-value,
    .data-table td,
    .card-dual strong {
      font-variant-numeric: tabular-nums
    }

    /* ── 2. Colour usage ── */

    /* Badges, tags and pills are neutral by default: quiet grey on grey. */
    .card-tag,
    .card-pill,
    .year-badge {
      background: var(--surface2);
      color: var(--text2);
      border: 1px solid var(--border);
      font-weight: 600;
      letter-spacing: .02em;
      border-radius: 999px
    }

    /* Colour is reserved for financial direction. */
    .card-pill.up {
      background: var(--green-bg);
      color: var(--green-ink);
      border-color: transparent
    }

    .card-pill.down {
      background: var(--red-bg);
      color: var(--red-ink);
      border-color: transparent
    }

    /* ── 3. Tables: a header, then rows, and one faint rule between them ──
       Nothing else is drawn. There are no vertical borders, no header fill and no
       banding: each row is closed by a single rgba(0,0,0,.05) line, the header label
       is the palest text in the card and the figures below it are the darkest. */
    .data-table th,
    .investment-table th {
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: var(--ls-label);
      text-transform: uppercase;
      word-break: keep-all;
      overflow-wrap: normal;
      hyphens: none;
      color: var(--text2);
      background: transparent;
      border-bottom: 1px solid var(--separator)
    }

    .data-table td,
    .investment-table td {
      border-color: var(--separator);
      color: var(--text);
      font-size: 13.5px
    }

    .data-table tbody tr:hover,
    .investment-table tbody tr:hover {
      background: var(--surface3)
    }

    .data-table .num,
    .investment-table .num {
      font-variant-numeric: tabular-nums
    }

    /* ── 4. Surfaces ── */
    .card,
    .cash-item,
    .property-card,
    .chart-wrap,
    .edit-table-wrap,
    .investment-table-wrap {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm)
    }

    .chart-wrap,
    .edit-table-wrap,
    .investment-table-wrap {
      box-shadow: none
    }

    .card:hover,
    .property-card:hover,
    .cash-item:not(.is-readonly):hover {
      border-color: var(--border2);
      box-shadow: var(--shadow-md)
    }

    /* ── 5. Controls ──
       The tab bar is Apple's segmented control: a light track with one dark pill that
       glides between the options (see moveTabIndicator in the script). */
    .dashboard-tabs {
      position: relative;
      display: flex;
      align-items: center;
      gap: 2px;
      width: fit-content;
      margin: 12px 0 28px;
      padding: 4px;
      background: var(--track);
      border-radius: 999px;
      box-shadow: inset 0 1px 2px rgba(0, 0, 0, .05)
    }

    .dashboard-tab-indicator {
      position: absolute;
      top: 4px;
      left: 0;
      height: calc(100% - 8px);
      width: 0;
      background: var(--text);
      border-radius: 999px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, .22);
      transform: translateX(0);
      transition: transform .38s var(--ease-tab), width .38s var(--ease-tab);
      will-change: transform, width
    }

    .dashboard-tab {
      position: relative;
      z-index: 1;
      padding: 8px 20px;
      border: 0;
      border-radius: 999px;
      background: transparent;
      cursor: pointer;
      font-size: var(--fs-card-title);
      font-weight: 500;
      color: var(--text2);
      transition: color .3s var(--ease-tab), font-weight .3s var(--ease-tab)
    }

    .dashboard-tab:hover {
      color: var(--text)
    }

    .dashboard-tab.active {
      background: transparent;
      color: #fff;
      font-weight: 600;
      box-shadow: none
    }

    /* Motion is decoration: respect a reduced-motion preference. */
    @media(prefers-reduced-motion:reduce) {
      .dashboard-tab-indicator {
        transition: none
      }
    }

    .btn {
      font-size: var(--fs-card-title);
      font-weight: 500;
      border-radius: 999px;
      padding: 9px 18px
    }

    .btn-primary {
      background: var(--accent);
      color: #fff
    }

    .btn-primary:hover {
      background: var(--accent-strong)
    }

    .btn-ghost {
      background: var(--surface);
      color: var(--text);
      border: 1px solid var(--border2)
    }

    .btn-ghost:hover {
      background: var(--surface2);
      border-color: var(--border2)
    }

    .btn-danger {
      background: var(--surface);
      color: var(--red);
      border: 1px solid var(--border2)
    }

    .edit-tab {
      font-size: var(--fs-card-title);
      font-weight: 500;
      color: var(--text2);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 999px
    }

    .edit-tab.active {
      background: var(--text);
      color: #fff;
      border-color: var(--text)
    }

    :focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px
    }

    /* ── 6. Layout rhythm & section colours ──
       Each section carries its own accent: a coloured heading, a left rail on the heading
       row and a very light wash at the top of the card. Same Apple type scale, but the
       page reads as a set of coloured tiles instead of one long grey column. */
    .section-block {
      --section-accent: var(--sec-slate);
      --section-tint: transparent;
      position: relative;
      margin-top: 40px;
      padding-top: 24px;
      border-top: 1px solid var(--border);
      background: linear-gradient(180deg, var(--section-tint), rgba(255, 255, 255, 0) 190px);
      border-radius: var(--radius-md)
    }

    /* The first card of a tab gets real inner padding, so its title never sits flush
       against the edge of the card. */
    .section-block.is-flush {
      margin-top: 0;
      border-top: 0;
      padding: 24px 28px
    }

    .section-block > .section-intro {
      padding-left: 13px;
      border-left: 3px solid var(--section-accent)
    }

    .section-block .section-heading {
      color: var(--section-accent)
    }

    /* The section's own colour, carried by its headline card, with room to breathe
       above the divider that closes the summary above it. */
    .section-block .cash-total.section-total {
      border-left: 3px solid var(--section-accent);
      margin-top: 22px;
      padding-top: 16px
    }

    /* Section palette: one accent and one tint per area of the dashboard. */
    .section--nw { --section-accent: var(--sec-pink); --section-tint: var(--sec-pink-tint) }
    .section--a { --section-accent: var(--sec-blue); --section-tint: var(--sec-blue-tint) }
    .section--b { --section-accent: var(--sec-teal); --section-tint: var(--sec-teal-tint) }
    .section--c { --section-accent: var(--sec-indigo); --section-tint: var(--sec-indigo-tint) }
    .section--d { --section-accent: var(--sec-green); --section-tint: var(--sec-green-tint) }
    .section--e { --section-accent: var(--sec-orange); --section-tint: var(--sec-orange-tint) }
    .section--f { --section-accent: var(--sec-purple); --section-tint: var(--sec-purple-tint) }
    .section--rev { --section-accent: var(--sec-blue); --section-tint: var(--sec-blue-tint) }
    .section--a1 { --section-accent: var(--sec-blue); --section-tint: var(--sec-blue-tint) }
    .section--a2 { --section-accent: var(--sec-purple); --section-tint: var(--sec-purple-tint) }
    .section--ref { --section-accent: var(--sec-orange); --section-tint: var(--sec-orange-tint) }
    .section--passive { --section-accent: var(--sec-green); --section-tint: var(--sec-green-tint) }
    .section--summary { --section-accent: var(--sec-indigo); --section-tint: var(--sec-indigo-tint) }
    .section--ins { --section-accent: var(--sec-indigo); --section-tint: var(--sec-indigo-tint) }
    .section--ret { --section-accent: var(--sec-teal); --section-tint: var(--sec-teal-tint) }

    /* ── One wash per tab ──
       Two tabs were carrying a separate colour per section, which made six palettes compete
       inside one screen. Production keeps the light blue its tables were already drawn in, and
       Net Worth takes Section C's own indigo, so each tab now reads as one colour. The override
       sits on the tab's own cards, which outranks the per-section palette above; the heading
       rail, the heading text and the headline total all follow the same accent, so a card is
       one colour rather than a colour plus a highlight. */
    #tab-revenue .section-block {
      --section-accent: var(--sec-blue);
      --section-tint: var(--sec-blue-tint)
    }

    #tab-networth .section-block {
      --section-accent: var(--sec-indigo);
      --section-tint: var(--sec-indigo-tint)
    }

    /* Two blocks that share one card (Revenue and its mix) are split by a rule, not a border. */
    .section-divider {
      height: 1px;
      margin: 30px 0 26px;
      background: linear-gradient(90deg, var(--border2), rgba(0, 0, 0, 0))
    }

    .section-intro {
      margin-bottom: 16px
    }

    .section-intro .section-heading-sub {
      margin-bottom: 0
    }

    /* ── 7. Section F: CPF layout ──
       Same shape as Section E: heading row, a 2 × 2 grid of account cards, then a totals strip
       and the chart. No extra wrapper card, so nothing is boxed inside another box. */
    .cpf-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 16px;
      margin-bottom: 20px
    }

    .cpf-total-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 12px;
      margin-bottom: 20px
    }

    .cpf-total-grid .cash-total {
      margin: 0
    }

    @media(max-width:600px) {
      .cpf-grid,
      .cpf-total-grid {
        grid-template-columns: 1fr
      }
    }

    /* Asset-class chips follow the same quiet treatment as every other tag. */
    .chip,
    .chip.reit,
    .chip.bank,
    .chip.etf,
    .chip.bond,
    .chip.tbill {
      background: var(--surface2);
      color: var(--text2);
      border: 1px solid var(--border);
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: .02em;
      border-radius: 999px;
      padding: 3px 10px
    }

    /* Lead lines and KPI figures inside modals follow the same scale. */
    .summary-card-desc {
      font-size: var(--fs-card-title);
      font-weight: 500;
      color: var(--text);
      margin: 4px 0 8px
    }

    .metric-value,
    .summary-card-value {
      font-size: var(--fs-value);
      font-weight: 600;
      letter-spacing: -.01em;
      font-variant-numeric: tabular-nums
    }

    .summary-card-label {
      font-size: var(--fs-sub);
      color: var(--text2)
    }

    /* Investment register: the account name links out to the platform it belongs to. */
    .investment-link {
      color: var(--text);
      font-weight: 600;
      text-decoration: none;
      border-bottom: 1px solid var(--border2);
      transition: var(--ease-out)
    }

    .investment-link:hover {
      color: var(--accent);
      border-bottom-color: var(--accent)
    }

    .investment-link::after {
      content: ' \2197';
      font-size: var(--fs-label);
      color: var(--text2)
    }

    /* ── Revenue Section A: prospects sit in one compact, collapsible group ── */
    .rev-group-row td {
      background: var(--surface2);
      padding: 6px 12px
    }

    .rev-group-toggle {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      border: 0;
      background: transparent;
      cursor: pointer;
      font: inherit;
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: var(--ls-label);
      text-transform: uppercase;
      color: var(--text2);
      padding: 2px 0
    }

    .rev-group-toggle:hover {
      color: var(--text)
    }

    .rev-group-count {
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: 999px;
      padding: 0 8px;
      font-size: var(--fs-label);
      letter-spacing: 0
    }

    .rev-group-hint {
      font-weight: 400;
      letter-spacing: 0;
      text-transform: none
    }

    .rev-row-compact td {
      padding-top: 4px;
      padding-bottom: 4px;
      font-size: 12px;
      color: var(--text2)
    }

    .rev-row-compact.rev-collapsed {
      display: none
    }

    /* ── Revenue dropdowns: Section A status, Section A.2 asset type & status ── */
    .rev-select-cell {
      /* The cell grows with its widest option, so the value picked in the dropdown is
         never clipped: once an item is chosen its full text stays readable. */
      width: auto;
      min-width: 116px;
      white-space: nowrap;
      padding-top: 4px;
      padding-bottom: 4px
    }

    .rev-select {
      /* Sized by its content (the widest option) instead of a fixed width, so the
         selected item always shows in full. */
      width: auto;
      max-width: 100%;
      border: 1px solid var(--border);
      border-radius: 999px;
      background: var(--surface2);
      color: var(--text);
      font: inherit;
      font-size: var(--fs-sub);
      font-weight: 500;
      padding: 3px 8px;
      cursor: pointer;
      transition: var(--ease-out)
    }

    .rev-select:hover {
      border-color: var(--border2)
    }

    .rev-select:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 1px
    }

    .rev-row-compact .rev-select {
      padding: 1px 6px
    }

    /* ── Section A: plain dropdown cells (Status) ──
       The Status column used to wear a pill-shaped <select> — a box, a background and an arrow —
       the moment the row was drawn. It now reads as plain text like every other cell: the box,
       border and arrow are stripped away, and the native menu only appears when the cell is
       clicked. The <select> is still the control, so the same handler still saves the pick. */
    .rev-plain-cell {
      width: auto;
      min-width: 0;
      white-space: nowrap
    }

    .rev-plain-input {
      appearance: none;
      -webkit-appearance: none;
      border: 0;
      background: transparent;
      color: inherit;
      font: inherit;
      padding: 1px 2px;
      border-radius: 5px;
      cursor: pointer
    }

    .rev-plain-input:hover {
      background: var(--surface2)
    }

    .rev-plain-input:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 1px
    }


    /* ── Section A.2: the Status badge ──
       What is read is a badge: a coloured dot and its label. The control itself is the <select>
       laid over the badge — invisible, but still the thing the browser opens and this app saves —
       so the two can never disagree. The dot is the badge's ::before, which keeps each status to
       one colour pair. */

    .rev-status-cell {
      position: relative
    }

    /* The dot itself, and nothing else: the label is gone, so the colour is the whole reading and
       the full status name is on the tooltip. */
    .rev-status-badge {
      display: inline-block;
      box-sizing: border-box;
      width: 11px;
      height: 11px;
      border: 1px solid transparent;
      border-radius: 50%;
      background: var(--text3);
      vertical-align: middle
    }

    /* Nothing picked yet: a dashed grey ring, so an unset status reads as still to do. */
    .rev-status-badge.is-unset {
      background: transparent;
      border-color: var(--border2);
      border-style: dashed
    }

    /* Too early to process — the deal is locked in, but there is nothing to market yet. */
    .rev-status-badge.is-pending {
      background: #a855f7
    }

    /* Being worked on right now. */
    .rev-status-badge.is-renting {
      background: #f59e0b
    }

    /* An existing tenant's renewal is in progress. */
    .rev-status-badge.is-renewing {
      background: #f97316
    }

    /* An existing tenant renewed. */
    .rev-status-badge.is-renewed {
      background: #22c55e
    }

    /* Sourcing a property for the client — active work alongside Renting. */
    .rev-status-badge.is-sourcing {
      background: #14b8a6
    }

    /* Closed: rented to, or found for, the client. */
    .rev-status-badge.is-rented {
      background: #3b82f6
    }

    /* The tenancy is over — the tenant has moved out. A dark slate dot reads as a closed case
       rather than a live one, and the row it sits on is greyed out beside it. */
    .rev-status-badge.is-terminated {
      background: #6b7280
    }

    /* A status saved by an older build that is no longer on the list. */
    .rev-status-badge.is-other {
      background: var(--text3)
    }

    /* The control that lies over the dot: the size of the whole cell, so any click in the column
       opens the picker. It is invisible and wears none of the pill chrome a .rev-select normally
       carries — no border, background, arrow or text — so the coloured dot beneath it is the only
       thing the column ever shows. */
    .rev-status-input {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      border: 0;
      border-radius: 0;
      background: transparent;
      color: transparent;
      font: inherit;
      appearance: none;
      -webkit-appearance: none;
      opacity: 0;
      cursor: pointer
    }

    /* Hovering the cell answers on the dot, and so does keyboard focus — the ring has to live on
       the dot because the element holding focus is the invisible select. A filled dot is already
       solid, so only the dashed "nothing picked yet" ring needs to answer on hover. */
    .rev-status-cell:hover .rev-status-badge.is-unset {
      border-color: var(--text3)
    }

    .rev-status-cell:focus-within .rev-status-badge {
      outline: 2px solid var(--accent);
      outline-offset: 1px
    }

    /* ── Remarks: plain text, and the text itself is what opens the editor ──
       Nothing here is dressed up like a button — the cell only sets the column width, and the
       single click lives on the text inside it, not on the whole cell. */
    /* Wide enough for a short phrase before the ellipsis — the space the shortening of the dates
       and the two compact dropdown columns freed up is spent here, which is where it is read. */
    .rev-remarks-cell {
      max-width: 110px
    }

    /* One line, cut off with an ellipsis; the whole remark is in the cell's native hover
       tooltip, so nothing is lost. */
    .rev-remarks-text {
      display: inline-block;
      max-width: 110px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      vertical-align: baseline;
      cursor: text;
      transition: var(--ease-out)
    }

    /* Section A's remarks editor is pinned to the cell it belongs to: it fills the column and never
       exceeds it, so opening a remark can neither widen the remarks column nor push the rest of the
       row sideways. The table keeps its widths and the row keeps its height while a cell is open. */
    #tab-revenue .rev-remarks-cell .cash-edit-input {
      width: 100%;
      max-width: 100%;
      box-sizing: border-box;
      position: relative;
      z-index: 2
    }

    /* Section A.2's remarks editor is taken out of the row altogether: it is anchored to the cell's
       inner corner, which is the edge beside the delete button, and floats over the columns to its
       left. The row keeps its height and every column keeps its width, so opening a remark can no
       longer stretch the table or push the columns off the right edge. */
    #tab-revenue .rental-table .rev-remarks-cell {
      position: relative
    }

    #tab-revenue .rental-table .rev-remarks-cell .cash-edit-input {
      position: absolute;
      top: 0;
      right: 0;
      width: 190px;
      max-width: none;
      z-index: 4
    }

    .rev-remarks-empty {
      color: var(--text3)
    }

    /* The no-repeat reason reads first in the Remarks column. It is bold within the same grey as
       the rest of the row, and the client's own remark follows after a separator. */
    .rev-no-repeat-remark {
      font-weight: 600
    }

    .rev-remarks-sep {
      color: var(--text3)
    }

    /* ── Section A.1's Files column ──
       The address of the deal's Google Drive folder lives on the row, not in this sheet: the
       column only reads "click" (or "Add link" before one is saved) and the pencil beside it opens
       the one dialog that edits the address, so the column stays one short word wide and a long
       Drive URL is never squeezed into it. */
    .rev-files-head {
      width: 64px
    }

    .rev-files-cell {
      white-space: nowrap;
      padding-top: 4px;
      padding-bottom: 4px
    }

    .rev-files-link {
      color: var(--accent);
      font-weight: 600;
      text-decoration: none;
      cursor: pointer
    }

    .rev-files-link:hover,
    .rev-files-link:focus-visible {
      text-decoration: underline
    }

    .rev-files-link:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
      border-radius: 3px
    }

    .rev-files-empty {
      color: var(--text3);
      cursor: pointer;
      transition: var(--ease-out)
    }

    .rev-files-empty:hover {
      color: var(--accent);
      text-decoration: underline
    }

    /* The pencil: quiet until the row is read, then plainly a button. */
    .rev-files-edit {
      margin-left: 4px;
      padding: 0 2px;
      border: 0;
      background: none;
      color: var(--text3);
      font: inherit;
      font-size: 11px;
      line-height: 1;
      opacity: .45;
      cursor: pointer;
      transition: var(--ease-out)
    }

    .rev-files-cell:hover .rev-files-edit,
    .rev-files-edit:hover,
    .rev-files-edit:focus-visible {
      opacity: 1;
      color: var(--accent)
    }

    /* Floating cards only exist while they are needed: no stray box can paint on the page. */
    .rev-remarks-popover {
      position: fixed;
      z-index: 60;
      max-width: 300px;
      padding: 10px 14px;
      background: var(--text);
      color: #fff;
      font-size: 12.5px;
      line-height: 1.45;
      border-radius: 12px;
      box-shadow: var(--shadow-md);
      opacity: 0;
      visibility: hidden;
      transform: translateY(4px);
      pointer-events: none;
      transition: opacity .16s ease-out, transform .16s ease-out, visibility .16s
    }

    .rev-remarks-popover.open {
      opacity: 1;
      visibility: visible;
      transform: translateY(0)
    }

    /* ── Section A.2: the Other side editor ──
       A small card, not a modal: it hangs off the Other side cell and writes one row's tenant or
       landlord-agent details. The width is fixed so the card can be placed beside the column
       without measuring a long name, and the fields stack so every label stays readable. */
    .rev-side-popover {
      position: fixed;
      z-index: 70;
      width: 286px;
      max-width: calc(100vw - 24px);
      max-height: calc(100vh - 24px);
      overflow-y: auto;
      padding: 14px;
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-lg);
      opacity: 0;
      visibility: hidden;
      transform: translateY(4px);
      pointer-events: none;
      transition: opacity .16s ease-out, transform .16s ease-out, visibility .16s
    }

    .rev-side-popover.open {
      opacity: 1;
      visibility: visible;
      transform: translateY(0);
      pointer-events: auto
    }

    .rev-side-editor-head {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 12px
    }

    .rev-side-editor-title {
      font-size: 13px;
      font-weight: 700;
      color: var(--text)
    }

    .rev-side-editor-sub {
      margin-left: auto;
      font-size: 11px;
      color: var(--text3)
    }

    .rev-side-close {
      width: 24px;
      height: 24px;
      padding: 0;
      border: 0;
      border-radius: 6px;
      background: transparent;
      color: var(--text3);
      font: inherit;
      font-size: 18px;
      line-height: 1;
      cursor: pointer;
      transition: var(--ease-out)
    }

    .rev-side-close:hover,
    .rev-side-close:focus-visible {
      background: var(--surface2);
      color: var(--text)
    }

    .rev-side-body {
      display: flex;
      flex-direction: column;
      gap: 10px
    }

    .rev-side-field {
      display: flex;
      flex-direction: column;
      gap: 4px
    }

    .rev-side-label {
      font-size: 11px;
      font-weight: 600;
      color: var(--text3)
    }

    .rev-side-input {
      width: 100%;
      box-sizing: border-box;
      padding: 7px 9px;
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: var(--radius-sm);
      box-shadow: inset 0 1px 2px rgba(0, 0, 0, .03);
      color: var(--text);
      font: inherit;
      font-size: 12.5px;
      transition: border-color var(--dur-fast) var(--ease-apple), box-shadow var(--dur-fast) var(--ease-apple)
    }

    .rev-side-input:focus {
      border-color: var(--accent);
      box-shadow: var(--focus-ring);
      outline: none
    }

    .rev-side-fields {
      display: flex;
      flex-direction: column;
      gap: 9px
    }

    .rev-side-fields[hidden] {
      display: none
    }

    .rev-side-hint {
      margin: 2px 0 0;
      font-size: 11px;
      line-height: 1.4;
      color: var(--text3)
    }

    .rev-side-actions {
      display: flex;
      justify-content: flex-end;
      gap: 8px;
      margin-top: 14px
    }

    .rev-side-actions .btn {
      padding: 6px 12px;
      font-size: 12px
    }

    /* Highlight the prospect group while a row is dragged over it. */
    .rev-group-row.rev-group-drop td {
      background: var(--surface);
      box-shadow: inset 0 0 0 2px var(--accent)
    }

    /* ── Section A.2: content-sized columns ──
       Every data cell reads on one line; headings wrap onto a second line, which is what lets a
       column be as narrow as its widest value instead of its longest heading. The column widths
       themselves are worked out against the card by autoFitColumns() in js/app.js; the rules here
       are the sheet the table wears before that first fit, and the floor it never goes below. */
    .rental-table {
      width: 100%;
      min-width: 0;
      table-layout: auto
    }

    .rental-table th {
      white-space: normal;
      word-break: normal;
      overflow-wrap: normal;
      hyphens: none;
      vertical-align: middle;
      line-height: 1.25;
      max-width: none;
      padding: 4px 12px;
      font-size: 11px;
      letter-spacing: .03em
    }

    .rental-table td {
      padding: 4px 12px;
      vertical-align: middle;
      white-space: nowrap
    }

    /* ── Section A.2: the renewal window ──
       Amber on the row is the one warning that still applies: the tenancy is inside the 90 days
       before its End date, so it is worth a call now. */
    .rev-days-cell.is-soon strong {
      color: var(--amber-ink)
    }

    /* A tenancy whose Days expiry has reached 0 or gone negative is finished business: the whole
       row takes the grey "closed" treatment (see rev-row-closed below) and its day count is muted
       too, so an expired tenancy reads as history rather than as something to act on. */
    .rev-days-cell.is-lapsed strong {
      color: var(--text3)
    }

    /* Renting, Sourcing and Upcoming are pre-tenancy work, so their Start Date, End Date and Days
       expiry are empty and read-only. The dashes stay muted so the row reads as "not yet" rather
       than as a tenancy with missing figures. */
    .rev-days-cell.is-no-dates strong,
    #tab-revenue .rental-table td.rev-date-locked {
      color: var(--text3)
    }

    #tab-revenue .rental-table td.rev-date-locked {
      cursor: default
    }

    /* ── Section A.2: the 30-day defects-due window ──
       A narrow cell between End Date and Receivable. The countdown is the reminder: quiet while
       there is time, amber on the last two days and the due day, red once the window has closed
       without a report (the tenant then pays for repairs), and grey again once a missed window is
       old history. The switch beside the count is the acknowledgement — one flip records today's
       date against the row, so "reported" is a fact with a date rather than a colour. */
    #tab-revenue .rental-table th.rev-defects-head {
      white-space: nowrap
    }

    #tab-revenue .rental-table td.rev-defects-cell {
      width: 1%;
      min-width: 92px;
      vertical-align: middle;
      font-variant-numeric: tabular-nums;
      transition: background .2s var(--ease-apple)
    }

    .rev-defects-body {
      display: flex;
      align-items: center;
      gap: 6px;
      white-space: nowrap
    }

    .rev-defects-text {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .01em;
      line-height: 1.1;
      color: var(--text2)
    }

    /* The small line under the count carries either the consequence of a missed window ("tenant
       pays") or the reason a cell reads N/A ("room" / "comm" / "ind" / "renew"), in the same ink
       as its count. */
    .rev-defects-note {
      display: block;
      margin-top: 1px;
      font-size: 9px;
      font-weight: 600;
      letter-spacing: .02em;
      text-transform: uppercase;
      opacity: .85
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-open .rev-defects-text {
      font-weight: 500
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-unset .rev-defects-text,
    #tab-revenue .rental-table td.rev-defects-cell.is-upcoming .rev-defects-text,
    #tab-revenue .rental-table td.rev-defects-cell.is-closed .rev-defects-text,
    #tab-revenue .rental-table td.rev-defects-cell.is-na .rev-defects-text {
      color: var(--text3);
      font-weight: 500
    }

    /* Renewals are existing tenancies continuing, so there is no defect report to switch. */
    #tab-revenue .rental-table td.rev-defects-cell.is-na .rev-defects-text {
      font-style: italic;
      letter-spacing: .04em
    }

    /* The reason joins N/A on one line ("N/A ROOM" / "N/A COMM" / "N/A IND" / "N/A RENEW") so the
       narrow cell does not grow a second row. The note beside an overdue count stays under the
       count. */
    #tab-revenue .rental-table td.rev-defects-cell.is-na .rev-defects-note {
      display: inline;
      margin-top: 0;
      margin-left: 3px
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-soon {
      background: var(--amber-bg);
      box-shadow: inset 0 0 0 1px rgba(255, 149, 0, .32)
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-soon .rev-defects-text {
      color: var(--amber-ink)
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-overdue {
      background: var(--red-bg);
      box-shadow: inset 0 0 0 1px rgba(255, 59, 48, .22)
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-overdue .rev-defects-text {
      color: var(--red-ink)
    }

    #tab-revenue .rental-table td.rev-defects-cell.is-done .rev-defects-text {
      color: var(--green-ink)
    }

    /* The switch itself: a 26×15 capsule that fills system green once the report is recorded. */
    .rev-defect-toggle {
      position: relative;
      display: inline-flex;
      flex: 0 0 auto;
      align-items: center;
      cursor: pointer
    }

    .rev-defect-toggle input {
      position: absolute;
      width: 1px;
      height: 1px;
      opacity: 0;
      pointer-events: none
    }

    .rev-defect-track {
      position: relative;
      display: block;
      width: 26px;
      height: 15px;
      border-radius: var(--radius-pill);
      background: var(--track);
      box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .06);
      transition: background .18s var(--ease-apple)
    }

    .rev-defect-knob {
      position: absolute;
      top: 2px;
      left: 2px;
      width: 11px;
      height: 11px;
      border-radius: 50%;
      background: #fff;
      box-shadow: 0 1px 2px rgba(0, 0, 0, .28);
      transition: transform .18s var(--ease-spring)
    }

    .rev-defect-toggle input:checked + .rev-defect-track {
      background: var(--green)
    }

    .rev-defect-toggle input:checked + .rev-defect-track .rev-defect-knob {
      transform: translateX(11px)
    }

    .rev-defect-toggle input:focus-visible + .rev-defect-track {
      outline: 2px solid var(--accent);
      outline-offset: 2px
    }

    .rev-defect-toggle input:disabled + .rev-defect-track {
      opacity: .4;
      box-shadow: none
    }

    .rev-defect-toggle input:disabled {
      cursor: not-allowed
    }

    /* ── The handover note (the small book beside the switch) ──
       Everything a tenant is told when the keys change hands sits behind this book: the landlord
       covers repairs in the first month, urgent faults are called in at once, and the rest goes on
       one list sent on day 29. It has to fit inside a 14px slot in the Defects due cell, so it is
       drawn quiet and only takes the accent on hover or focus — findable without becoming a sixth
       badge in the row. The copy in the column head is the blank template. */
    .rev-handover-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 16px;
      height: 16px;
      padding: 0;
      margin-left: 2px;
      border: 0;
      border-radius: 4px;
      background: transparent;
      color: var(--text2);
      opacity: .75;
      cursor: pointer;
      vertical-align: middle;
      transition: color .15s var(--ease-apple), background .15s var(--ease-apple), opacity .15s var(--ease-apple)
    }

    .rev-handover-btn svg {
      display: block;
      width: 13px;
      height: 13px
    }

    .data-table tr:hover .rev-handover-btn {
      opacity: 1
    }

    .rev-handover-btn:hover,
    .rev-handover-btn:focus-visible {
      opacity: 1;
      color: var(--accent);
      background: var(--accent-bg)
    }

    .rev-handover-btn:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 1px
    }

    /* In the head the book sits at the end of the unit line, a shade lower so the column still
       reads "Defects due 30-day report" first. */
    th.rev-defects-head .rev-handover-btn {
      width: 14px;
      height: 14px;
      margin-left: 4px;
      opacity: .75;
      vertical-align: -2px
    }

    th.rev-defects-head .rev-handover-btn svg {
      width: 12px;
      height: 12px
    }

    /* A row with no window has no note: N/A cells read as closed and nothing sits beside them. */
    td.rev-defects-cell.is-na .rev-handover-btn {
      display: none
    }

    .rev-row-expiring td {
      background: rgba(255, 149, 0, .12)
    }

    .rev-row-expiring td:first-child {
      box-shadow: inset 3px 0 0 var(--amber)
    }

    /* ── Sections A.1 and A.2: live work ──
       A deal being bought or sold (A.1) and a tenancy being actively rented, sourced or renewed
       (A.2) share one yellow-gold wash, so every row that is actively being worked on reads the
       same at a glance. An upcoming tenancy gets its own green treatment: the deal is locked in
       but the work has not started. A renewed, completed (Rented / Found), pending or closed row
       stays plain. */
    .rev-row-working td,
    .rev-row-active td {
      background: rgba(255, 199, 0, .18)
    }

    .rev-row-working td:first-child,
    .rev-row-active td:first-child {
      box-shadow: inset 3px 0 0 #d4a017
    }

    .data-table tr.rev-row-working:hover td,
    .data-table tr.rev-row-active:hover td {
      background: rgba(255, 199, 0, .26)
    }

    /* Upcoming: a future tenancy that is already locked in. Green marks it as accepted work
       waiting to start, distinct from the gold rows that are being worked on right now. */
    .rev-row-upcoming td {
      background: var(--green-bg)
    }

    .rev-row-upcoming td:first-child {
      box-shadow: inset 3px 0 0 var(--green)
    }

    .data-table tr.rev-row-upcoming:hover td {
      background: #d7ecd9
    }

    /* The defects warning keeps its own wash on a row that is also active or hovered: the gold
       work-in-progress tint (above) and the close-row hover must not paint over a closed defect
       window. Same specificity, later in the sheet, so the warning wins. */
    #tab-revenue .rental-table tr td.rev-defects-cell.is-soon {
      background: var(--amber-bg)
    }

    #tab-revenue .rental-table tr td.rev-defects-cell.is-overdue {
      background: var(--red-bg)
    }

    /* ── Year Summary: income tax is a deduction, never income ──
       The tax line stays editable, but it is washed red and its figures carry a minus sign so the
       Total (which subtracts it) cannot be misread as spending money that is still in the year. */
    #tab-revenue .rev-tax-row td {
      background: rgba(255, 59, 48, .045)
    }

    #tab-revenue .rev-tax-row td.rev-tax-cell {
      color: var(--red-ink);
      font-weight: 600
    }

    /* ── Year Summary: computed ratio column ── */
    .rev-ratio-cell {
      width: 64px;
      color: var(--text2);
      font-variant-numeric: tabular-nums
    }

    /* A.1 and A.2 count their receivable as well as cash received. The split reads as a small
       second line under the amount, so the column still says how much of the figure is still due. */
    .rev-summary-due {
      display: block;
      margin-top: 1px;
      font-size: 9px;
      line-height: 1.2;
      font-weight: 500;
      letter-spacing: .01em;
      color: var(--text3);
      white-space: nowrap
    }

    /* ── Revenue tab: rows are about half as tall ──
       Only the row padding and line-height change — the gaps between the sections and their
       headings are left exactly as they are. Scoped to #tab-revenue so the shared .data-table
       used by every other tab keeps its original spacing. Ordered general → specific, so the
       narrower row types win. */
    #tab-revenue .data-table td { padding: 1px 12px; line-height: 1.2 }
    #tab-revenue .data-table th { padding: 2px 12px; line-height: 1.15 }
    #tab-revenue .rental-table td { padding: 4px 12px }
    #tab-revenue .rental-table th { padding: 4px 12px }
    #tab-revenue .rev-group-row td { padding: 1px 12px }
    #tab-revenue .rev-row-compact td { padding-top: 1px; padding-bottom: 1px }
    #tab-revenue td.rev-select-cell { padding-top: 1px; padding-bottom: 1px }

    /* ── Section A.1: fourteen columns, packed to their content ──
       A.1 is the first thing on the Revenue tab and has to be read without panning, so its columns
       are pinned by their own contents rather than spread across the card. Two rules do the work,
       and both are only what the table wears until autoFitColumns() (js/app.js) writes the measured
       widths on it:
         · width:1px on every heading and cell is the table-layout trick for "take only what the
           content needs" — the browser still honours the min/max-width on the columns below, so no
           column ends up narrower than its floor;
         · Property is the one elastic column (width:auto) so it absorbs the spare width, up to the
           cap the shared property cell already carries.
       Headings wrap onto a second line (the "(S$)" unit already reads on one of its own) instead of
       widening their column; a value never wraps — the address folds onto a second line at the most,
       and anything that still does not fit is cut with an ellipsis. */
    #tab-revenue #revenue-service-panel .data-table {
      width: 100%;
      min-width: 0;
      table-layout: auto
    }

    #tab-revenue #revenue-service-panel .data-table th,
    #tab-revenue #revenue-service-panel .data-table td {
      width: 1px;
      padding-left: 6px;
      padding-right: 6px
    }

    #tab-revenue #revenue-service-panel .data-table th { white-space: normal }
    #tab-revenue #revenue-service-panel .data-table td { white-space: nowrap }

    /* Property is elastic, but never so narrow that an address has to break mid-word. */
    #tab-revenue #revenue-service-panel .data-table th:nth-child(2),
    #tab-revenue #revenue-service-panel .data-table td.rev-property-cell {
      width: auto;
      min-width: 190px;
      max-width: 340px
    }

    /* ── One line per value, in every Production table ──
       Property is the only cell allowed a second line (see "The Property column" below). A client,
       a referrer, a remark and every date and amount read on one line whatever the screen is
       doing, so a column is exactly as wide as the widest thing it shows and never wider. The
       columns are then sized against the card by autoFitColumns() in js/app.js. */
    #tab-revenue #revenue-service-panel .data-table .rev-property-text {
      width: auto
    }

    /* The plain Status dropdown sizes to its own option instead of a fixed minimum, so its
       column costs only the width of the longest status word. */
    #tab-revenue #revenue-service-panel .data-table .rev-select-cell { min-width: 0 }

    /* Room for the corner arrow that marks a dropdown cell (see "The corner arrow" below). The mark
       is drawn inside the cell's right edge, so that edge is given the width to hold it instead of
       letting the chevron crowd the value. A.1's cells are padded at 6px; A.2 already pads 12px. */
    #tab-revenue #revenue-service-panel .data-table td.rev-select-cell,
    #tab-revenue #revenue-service-panel .data-table td.rev-asset-cell {
      padding-right: 12px
    }

    /* ── Section A.2: the widest table on the page ──
       The ID in the selector beats .data-table's width:100%, and margin-right:auto keeps the table
       left-aligned when it is narrower than the card. A table wider than the card is panned by the
       .edit-table-wrap scrollbar — but autoFitColumns() works the columns out so that it rarely has
       to be. */
    #tab-revenue #revenue-rental-panel .rental-table {
      min-width: 0;
      table-layout: auto;
      margin-right: auto;
      margin-left: 0
    }

    /* Every column except Property shrinks to its own text. width:1px is the table-layout trick
       for "take only what the content needs"; nowrap above stops that content from wrapping.
       If the table is ever given width:100%, these columns stay tight and Property takes the
       spare width instead of every short column being padded apart. */
    #tab-revenue .rental-table th:not(.rev-property-head):not(.rev-actions-head),
    #tab-revenue .rental-table td:not(.rev-property-cell):not(.rev-row-actions) {
      width: 1px
    }

    /* Property is the one elastic column: auto width lets it absorb leftover space in a
       full-width table, while max-content above keeps it at address width when compact. */
    #tab-revenue .rental-table th.rev-property-head,
    #tab-revenue .rental-table td.rev-property-cell {
      width: auto
    }

    /* No cell wraps — a heading may, and Property may take a second line. Each column is the
       width its widest value needs, and the table returns any spare width to the address. */
    #tab-revenue .rental-table td,
    #tab-revenue .rental-table td[data-revtype="date"],
    #tab-revenue .rental-table td.rev-date-locked { white-space: nowrap }

    /* Section A.1's Exercised Date reads on one line the way Section A.2's two dates do. */
    #tab-revenue .data-table th.rev-date-head,
    #tab-revenue .data-table td.rev-date-cell { white-space: nowrap }

    /* ── The Property column: the whole address, two lines at the most ──
       Sections A.1, A.2 and B share the property cell, and it is the one column that gives: the
       widths around it are worked out from the screen (autoFitColumns in js/app.js) and whatever is
       left over goes to the address. It reads on one line whenever the screen can hold it and folds
       onto a second line when it cannot.

       Nothing is ever cut off. A shortened address in the column the whole sheet is read by is
       worse than a taller row, so the text wraps and the column is held wide enough by the fit to
       need no more than the two lines — autoFitColumns keeps the column at half the longest
       address's single-line width, which is exactly the width that folds it onto two. Should a
       screen be too narrow even for that, the address takes the third line rather than losing its
       tail. */
    #tab-revenue .data-table .rev-property-cell { max-width: 340px }

    #tab-revenue table.col-widths-applied .rev-property-cell { max-width: none }

    #tab-revenue .rev-property-text {
      display: block;
      white-space: normal;
      overflow-wrap: break-word;
      word-break: break-word
    }

    /* ── All four Production sheets read the same way ──
       A heading wraps (its unit label rides a second line); a value never does, except the address
       above. Sections B, C and the Year Summary have no column rules of their own, so this is where
       they get the treatment A.1 and A.2 already had: one line per value, every column as narrow as
       the widest thing in it, and the widths then fitted to the card by autoFitColumns(). */
    #tab-revenue .edit-table-wrap th { white-space: normal }
    #tab-revenue .edit-table-wrap td { white-space: nowrap }

    /* ── A value that does not fit is cut, not wrapped ──
       The column widths are worked out against the card, so on a screen too small for the table some
       column has to lose something. It loses its tail rather than its row height: a long remark or a
       client's full name is cut with an ellipsis, one line high, and the cell's title carries the
       value in full. Only the free-text columns are ever made narrower than their content (see
       autoFitColumns); a date, an amount, a status or a file link keeps the width it needs. */
    #tab-revenue .edit-table-wrap td { overflow: hidden; text-overflow: ellipsis }

    /* While a client or a referrer is being edited, the input is deliberately wider than its cell —
       it lies over its neighbours so the whole name can be typed without the column moving. */
    #tab-revenue .edit-table-wrap td.rev-wide-edit-cell { overflow: visible; text-overflow: clip }

    /* Section A.2 opens the property database from the column heading instead of from the address
       in the row, so the heading carries the click and the cell stays plain text. */
    .rev-property-head {
      cursor: pointer
    }

    .rev-property-head:hover {
      color: var(--accent);
      text-decoration: underline dotted;
      text-underline-offset: 2px
    }

    /* A zero amount reads as a light dash instead of "S$0.00". */
    #tab-revenue .data-table td.rev-zero { color: var(--text3) }

    /* The "(S$)" sits on its own lighter line under the column name, so the header carries the
       currency without widening the column. */
    #tab-revenue .data-table th .th-unit {
      display: block;
      font-weight: 500;
      color: var(--text3)
    }

    #tab-revenue .rental-table .rev-select-cell {
      min-width: 104px
    }

    /* The scroll container owns horizontal overflow: a compact table sits at the left edge, and
       a table wider than the card scrolls here rather than pushing the page sideways. */
    #revenue-rental-panel {
      width: 100%;
      max-width: 100%;
      overflow-x: auto
    }

    /* Section A.2's Status dot and its Rep codes are the two columns that hand width back: the
       dot needs no more room than the dot, and "LL" / "TT" read as plain text once the picker's
       border, background and arrow are taken away. Both keep their <select>, so a click still
       opens the native menu and the same handler still saves the pick. */
    #tab-revenue .rental-table .rev-status-cell,
    #tab-revenue .rental-table .rev-rep-cell {
      width: 1%;
      min-width: 0
    }

    .rev-rep-input {
      appearance: none;
      -webkit-appearance: none;
      border: 0;
      background: transparent;
      padding: 0 2px;
      text-align: center;
      font-variant-numeric: tabular-nums
    }

    .rev-rep-input:hover,
    .rev-rep-input:focus {
      background: var(--surface2)
    }


    /* ── Section A.2's Asset type cell ──
       The column reads as plain text — no box and no arrow, so it is only as wide as the words in
       it. The picker appears on the click and floats over the column beside it (the cell keeps the
       width its text needed), so opening it cannot move the row. */

    .rev-asset-cell {
      position: relative;
      cursor: pointer;
      white-space: nowrap
    }

    .rev-asset-cell:hover {
      background: var(--surface2)
    }

    .rev-asset-cell.is-empty {
      color: var(--text3)
    }

    .rev-asset-picker {
      position: absolute;
      top: 0;
      left: 0;
      max-width: none;
      z-index: 3
    }


    /* ── The corner arrow: the only hint a dropdown cell wears ──
       A.1's Status, A.2's Status dot, A.2's Rep codes and both Asset type cells read as plain text —
       no box, no border and no native arrow — which is exactly what keeps their columns as narrow as
       their words. That left nothing at all to say the cell opens a picker, so each one carries one
       small chevron in its bottom-right corner. It is drawn inside the cell's own padding, so it
       costs no width and can never sit on the text; it is quiet at rest and answers the pointer, and
       it lets every click through to the control underneath, so the corner is still part of the
       dropdown rather than a dead spot in it. */
    .rev-select-cell,
    .rev-asset-cell {
      position: relative;
      /* The whole cell answers the pointer now, so the whole cell wears the pointer. */
      cursor: pointer
    }

    .rev-select-cell::after,
    .rev-asset-cell::after {
      content: '';
      position: absolute;
      right: 4px;
      bottom: 2px;
      width: 0;
      height: 0;
      border-left: 3px solid transparent;
      border-right: 3px solid transparent;
      border-top: 3.5px solid var(--text3);
      opacity: .45;
      pointer-events: none;
      transition: opacity .12s ease-out
    }

    .rev-select-cell:hover::after,
    .rev-select-cell:focus-within::after,
    .rev-asset-cell:hover::after {
      opacity: .85
    }


    /* ── Sections A.1 and A.2: the year tabs ──
       The same segmented control as the dashboard's tab bar, sized to sit over the section heading.
       It picks which year's sheet the table below is showing — the sales records of Section A.1 or
       the tenancies of Section A.2. */

    .rev-year-tabs {
      position: relative;
      display: flex;
      align-items: center;
      gap: 2px;
      width: fit-content;
      /* The strip sits below the section heading and above the table. Its left margin lines it up
         with the heading (13px padding + 3px accent bar); the section-intro above it supplies the
         gap, so only the space down to the table is set here. */
      margin: 0 0 12px 16px;
      padding: 3px;
      background: var(--track);
      border-radius: 999px;
      box-shadow: inset 0 1px 2px rgba(0, 0, 0, .05)
    }

    .rev-year-tab {
      padding: 4px 16px;
      border: 0;
      border-radius: 999px;
      background: transparent;
      color: var(--text2);
      font: inherit;
      font-size: var(--fs-sub);
      font-weight: 500;
      font-variant-numeric: tabular-nums;
      cursor: pointer;
      transition: var(--ease-out)
    }

    .rev-year-tab:hover {
      color: var(--text)
    }

    .rev-year-tab.active {
      background: var(--text);
      color: #fff;
      font-weight: 600;
      box-shadow: 0 2px 10px rgba(0, 0, 0, .22)
    }

    /* ── Year Summary, Average commission, Income history and Client Network ──
       The four read-outs share one row on a Studio Display, where there is room for a fourth
       card without squeezing the tables. Below that the first three keep their own row and the
       network drops underneath it, as before. The row is a flex line rather than fixed
       fractions, so the columns take only the width they need and the gaps close up on a
       narrower screen. */
    .rev-summary-row {
      min-width: 0
    }

    .rev-summary-grid {
      display: flex;
      flex-wrap: wrap;
      align-items: flex-start;
      gap: 14px
    }

    /* The lifetime average holds one short table, so it stays narrower than the summary table
       and stops growing once it has a comfortable reading width. */
    .rev-averages {
      flex: 1 1 340px;
      min-width: 300px;
      max-width: 480px;
      margin-top: 0;
      align-self: flex-start
    }

    /* The bar chart takes whatever is left beside the two tables on a wide screen, and a full
       row of its own once the three no longer fit. */
    .rev-history-card {
      flex: 1 1 320px;
      min-width: 300px
    }

    .income-history-chart {
      width: 100%;
      height: auto;
      display: block
    }

    /* The donut legend is a column; the bar chart's own legend reads better as a wrapped row. */
    .chart-inline-legend {
      flex-direction: row;
      flex-wrap: wrap;
      gap: 6px 16px;
      margin-top: 12px
    }

    /* The bar chart's grand total closes that row, set off from the per-stream figures by a rule
       and lifted out in the body text colour so the whole-history figure is the one that reads. */
    .legend-item-total {
      color: var(--text);
      border-left: 1px solid var(--border);
      padding-left: 16px
    }

    .legend-item-total strong {
      font-weight: 600
    }

    @media(max-width:900px) {
      .rev-summary-grid {
        display: grid;
        grid-template-columns: minmax(0, 1fr)
      }

      .rev-averages,
      .rev-history-card {
        max-width: none;
        min-width: 0
      }
    }

    /* The network joins the row as its fourth card only when the section itself is wide
       enough for four readable columns: 2240px of section width is the measured point at
       which the Year Summary's item names still fit on one line, its narrowest card being
       550px there. Below it the network keeps its old full-width row under the other three.
       The query matches the Revenue section's own size container (Section 8), not the
       viewport, so it follows the shell's real width rather than the monitor's. */
    .rev-network-card {
      min-width: 0
    }

    .rev-network-card > .section-intro {
      padding-left: 13px;
      border-left: 3px solid var(--section-accent)
    }

    @container (min-width: 2240px) {
      .rev-summary-row {
        display: grid;
        grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
        gap: 14px;
        align-items: start
      }

      .rev-summary-grid {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 14px;
        align-items: start
      }

      .rev-summary-table-col,
      .rev-averages,
      .rev-history-card {
        min-width: 0;
        max-width: none
      }

      .rev-network-card {
        align-self: start;
        padding: 16px 18px 14px;
        background: var(--bg);
        border: 1px solid var(--border);
        border-radius: var(--radius-lg);
        box-shadow: var(--shadow-sm)
      }

      .rev-network-divider {
        display: none
      }

      .rev-network-card > .section-intro {
        align-items: flex-start;
        margin-bottom: 10px
      }

      .rev-network-card .section-heading-sub {
        font-size: 11px;
        line-height: 1.45
      }

      .rev-network-card .client-network-bar {
        margin-top: 8px
      }

      .rev-network-card .client-network-hint {
        margin-top: 3px
      }

      .rev-network-card .client-network-shell {
        margin-top: 8px;
        gap: 10px;
        grid-template-columns: minmax(0, 1fr)
      }

      .rev-network-card .client-network-wrap {
        max-width: 500px
      }

      .rev-network-card .cn-filter {
        padding: 11px 12px 12px
      }

      .rev-network-card .cn-filter-block {
        margin-top: 10px;
        padding-top: 8px
      }

      .rev-network-card .cn-type {
        padding: 6px 8px;
        font-size: 10.5px
      }

      .rev-network-card .chart-inline-legend {
        gap: 4px 10px;
        margin-top: 6px
      }

      /* The rail under a 530px-wide card is read as a compact two-column panel: Type on the
         left, the three ranges stacked on the right. The long explanatory notes stay in the
         labels' tooltips here so the card does not grow taller than its diagram. */
      .rev-network-card .cn-filter {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: 14px;
        align-items: start
      }

      .rev-network-card .cn-filter-head {
        grid-column: 1 / -1;
        grid-row: 1
      }

      .rev-network-card .cn-filter-block {
        min-width: 0
      }

      .rev-network-card .cn-filter-block--type {
        grid-column: 1;
        grid-row: 2 / span 3
      }

      .rev-network-card .cn-filter-block--ref {
        grid-column: 2;
        grid-row: 2
      }

      .rev-network-card .cn-filter-block--year {
        grid-column: 2;
        grid-row: 3
      }

      .rev-network-card .cn-filter-block--commission {
        grid-column: 2;
        grid-row: 4
      }

      .rev-network-card .cn-filter-note {
        display: none
      }

      .rev-network-card .chart-inline-legend {
        gap: 6px 10px;
        margin-top: 8px
      }
    }

    /* The Add summary line button follows the table in the same column, so it no longer waits
       below the taller Income history card. */
    .rev-summary-table-col {
      display: flex;
      flex: 1 1 500px;
      flex-direction: column;
      gap: 12px;
      min-width: 0
    }

    .rev-summary-table-col .btn-row {
      margin: 0
    }

    /* Six narrow columns share the three-up shell, and A.1 / A.2 now carry a small due note under
       their income. Tightening the gutters keeps the table inside the column instead of asking for
       its own sideways scroll at 1440px. */
    #tab-revenue .rev-summary-table-col .data-table th,
    #tab-revenue .rev-summary-table-col .data-table td {
      padding-left: 8px;
      padding-right: 8px
    }

    /* ── Year Summary: average commission per case ──
       A compact read-out beside the summary table. It reads all years on file, so it is
       deliberately outside the year strip and does not change when another year is picked. */
    .rev-averages {
      margin-top: 0;
      padding: 16px 18px 12px;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm)
    }

    .rev-averages-head {
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      gap: 4px;
      margin-bottom: 10px
    }

    .rev-averages .chart-title {
      margin-bottom: 4px
    }

    .rev-averages-note {
      margin: 0;
      max-width: 780px;
      font-size: 11px;
      line-height: 1.45;
      color: var(--text3)
    }

    .rev-averages-scope {
      flex: none;
      padding-top: 0;
      font-size: 11px;
      color: var(--text2);
      text-align: left;
      white-space: nowrap;
      font-variant-numeric: tabular-nums
    }

    .rev-averages .edit-table-wrap {
      background: transparent;
      border: 0;
      border-radius: 0;
      box-shadow: none
    }

    #tab-revenue .rev-averages .data-table {
      table-layout: fixed
    }

    #tab-revenue .rev-averages .data-table th {
      padding: 4px 8px;
      line-height: 1.2;
      white-space: normal;
      border-bottom-color: var(--border)
    }

    /* The average card is the narrowest of the three columns, so the price unit line is a step
       smaller than the shared table header's. It still reads as a caption under the column name. */
    #tab-revenue .rev-averages .data-table th .th-unit {
      font-size: 9px;
      letter-spacing: .01em
    }

    #tab-revenue .rev-averages .data-table td {
      padding: 4px 8px;
      line-height: 1.15;
      border-bottom-color: var(--border)
    }

    #tab-revenue .rev-averages .data-table th:first-child,
    #tab-revenue .rev-averages .data-table td:first-child {
      width: 34%;
      padding-left: 2px
    }

    #tab-revenue .rev-averages .data-table th.num,
    #tab-revenue .rev-averages .data-table td.num {
      width: 33%;
      padding-right: 2px
    }

    .rev-averages-count {
      display: block;
      margin-top: 0;
      font-size: 9.5px;
      font-weight: 500;
      line-height: 1.2;
      color: var(--text3)
    }

    #tab-revenue .rev-averages .data-table .rev-averages-empty {
      color: var(--text3)
    }

    #tab-revenue .rev-averages .data-table .rev-averages-total td {
      border-top: 1px solid var(--border2);
      border-bottom: 0;
      background: var(--surface2);
      font-weight: 600
    }

    @media(max-width:620px) {
      .rev-averages-head {
        flex-direction: column;
        gap: 4px
      }

      .rev-averages-scope {
        text-align: left
      }

      #tab-revenue .rev-averages .data-table th:first-child,
      #tab-revenue .rev-averages .data-table td:first-child {
        width: 34%
      }

      #tab-revenue .rev-averages .data-table th.num,
      #tab-revenue .rev-averages .data-table td.num {
        width: 33%
      }
    }

    /* The × button is display:flex, which is block-level; keeping it inline stops it from
       pushing the row taller than one line. */
    #tab-revenue .delete-row-btn {
      display: inline-flex;
      vertical-align: middle
    }

    /* × is the only control left in the action column, so it no longer needs the 92px the
       move buttons used to require. */
    #tab-revenue .data-table .rev-row-actions,
    #tab-revenue .data-table .rev-actions-head {
      width: 40px;
      min-width: 40px;
      padding-right: 4px
    }

    #tab-revenue .row-move-btn {
      vertical-align: middle
    }

    /* Any deal or tenancy that still owes money shows its Receivable in red. */
    #tab-revenue .data-table td.rev-outstanding {
      color: var(--red-ink)
    }

    /* ── Year Summary: a row linked to a section is read-only and fed by that section's table ── */
    #tab-revenue .rev-linked-cell {
      background: rgba(29, 29, 31, .035);
      cursor: default;
      color: var(--text2)
    }

    /* ── Goal bars: progress toward a target, one under each donut ── */
    .goal-bar {
      margin-top: 16px;
      padding-top: 12px;
      border-top: 1px solid var(--border)
    }

    .goal-bar-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 10px;
      font-size: 12px;
      color: var(--text2)
    }

    .goal-bar-head strong {
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .goal-bar-track {
      height: 8px;
      margin-top: 8px;
      border-radius: 99px;
      background: var(--border);
      overflow: hidden
    }

    .goal-bar-fill {
      width: 0;
      height: 100%;
      border-radius: 99px;
      background: var(--accent);
      transition: width .4s ease-out
    }

    /* The passive bar carries the passive section colour; the active bar keeps the blue accent. */
    .goal-bar.is-passive .goal-bar-fill {
      background: var(--sec-green)
    }

    .goal-bar-note {
      margin-top: 6px;
      font-size: 11px;
      color: var(--text3)
    }

    /* ── Property database: a wide panel of inline-editable records ── */
    .modal.modal-wide {
      width: min(1080px, 100vw)
    }

    .property-book-wrap {
      overflow-x: auto
    }

    .property-book {
      min-width: 900px
    }

    .property-book th,
    .property-book td {
      padding: 4px 6px
    }

    .property-book .rev-row-actions,
    .property-book .rev-actions-head {
      width: 40px;
      min-width: 40px;
      padding-right: 4px
    }

    .property-book-address-cell {
      display: flex;
      align-items: center;
      gap: 6px
    }

    .property-book-input {
      width: 100%;
      border: 1px solid transparent;
      border-radius: 6px;
      background: transparent;
      color: var(--text);
      font: inherit;
      font-size: 13px;
      padding: 3px 6px;
      outline: none;
      transition: var(--ease-out)
    }

    .property-book-input::placeholder {
      color: var(--text3)
    }

    .property-book-input:hover {
      border-color: var(--border)
    }

    .property-book-input:focus {
      border-color: var(--accent);
      background: var(--surface)
    }

    .property-book-open {
      flex-shrink: 0;
      color: var(--accent);
      font-size: 15px;
      line-height: 1;
      text-decoration: none
    }

    .property-book-row.is-current td {
      background: rgba(0, 122, 255, .07)
    }

    /* ── Clients: the name in the table is the way in, and the drawer shows the history ── */
    .rev-client-name {
      cursor: pointer;
      border-bottom: 1px dashed transparent;
      transition: var(--ease-out)
    }

    .rev-client-name:hover {
      color: var(--accent);
      border-bottom-color: var(--accent)
    }

    /* A client marked as one not to work with again. The line stays on hover, so the warning is
       never lost while the name is being clicked into the profile. */
    .rev-client-name.is-no-repeat {
      color: var(--text3);
      text-decoration: line-through;
      text-decoration-thickness: 1.5px;
      text-decoration-color: currentColor
    }

    .rev-client-name.is-no-repeat:hover {
      color: var(--accent);
      text-decoration: line-through
    }

    .rev-client-empty {
      color: var(--text3)
    }

    /* ── Section A.2: the Other side cell ──
       One compact line: a small badge names the side, then the person or agent, then the WhatsApp
       mark when a number is recorded. The badge does the explaining, so the cell needs no heading
       beyond "Other side". A blank Rep is not an editable cell — it points the reader at Rep first. */
    .rev-side-head,
    .rev-side-cell {
      width: 1%;
      min-width: 0;
      white-space: nowrap
    }

    .rev-side-cell {
      max-width: 210px;
      cursor: pointer
    }

    .rev-side-cell.is-rep-unset {
      cursor: default
    }

    .rev-side-main {
      display: flex;
      align-items: center;
      gap: 5px;
      min-width: 0
    }

    .rev-side-badge {
      display: inline-flex;
      flex: none;
      align-items: center;
      padding: 1px 6px;
      border-radius: 999px;
      background: var(--surface2);
      color: var(--text2);
      font-size: 10.5px;
      font-weight: 600;
      line-height: 1.5;
      letter-spacing: .01em
    }

    .rev-side-badge.is-direct {
      background: var(--accent-bg);
      color: var(--accent)
    }

    .rev-side-name {
      min-width: 0;
      max-width: 118px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap
    }

    .rev-side-empty {
      color: var(--text3);
      font-size: 12px
    }

    .rev-side-cell:hover .rev-side-empty {
      color: var(--accent)
    }

    .rev-side-wa {
      flex: none;
      width: 20px;
      height: 20px
    }

    .rev-side-wa svg {
      width: 14px;
      height: 14px
    }

    /* ── Contact: WhatsApp and a call, one tap each ──
       Two links sit beside the client's name. The WhatsApp mark keeps the service's green and the
       handset takes the system blue, so the pair reads at a glance without a label; both sit on a
       soft tint when hovered or focused. Only the marks take up width, so the column costs almost
       nothing in Sections A.1 and A.2. */
    .rev-contact-head,
    .rev-contact-cell {
      width: 1%;
      min-width: 0;
      white-space: nowrap;
      text-align: center
    }

    .rev-contact-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 22px;
      height: 22px;
      border-radius: 6px;
      color: var(--text3);
      text-decoration: none;
      transition: var(--ease-out)
    }

    .rev-contact-btn + .rev-contact-btn {
      margin-left: 2px
    }

    .rev-contact-btn svg {
      display: block;
      width: 15px;
      height: 15px
    }

    .rev-contact-wa {
      color: #25d366
    }

    .rev-contact-wa:hover,
    .rev-contact-wa:focus-visible {
      background: var(--green-bg);
      color: #128c4a
    }

    .rev-contact-call {
      color: var(--accent)
    }

    .rev-contact-call:hover,
    .rev-contact-call:focus-visible {
      background: var(--accent-bg);
      color: var(--accent-active)
    }

    /* A client or referrer name is usually longer than its narrow column, so while it is being
       edited the field is drawn twice the cell's width and floats out over the columns to its
       left. The column keeps its width and the row keeps its height, so opening the editor does
       not move anything — the whole name simply reads and types at once. */
    .rev-client-cell.rev-wide-edit-cell {
      position: relative
    }

    .rev-client-cell.rev-wide-edit-cell .cash-edit-input {
      position: absolute;
      top: 0;
      right: 0;
      width: 200%;
      max-width: none;
      z-index: 6
    }

    /* The client drawer is the same side panel as the modals, just narrower. */
    .modal.modal-drawer {
      width: min(430px, 100vw)
    }

    .client-profile {
      display: flex;
      flex-direction: column;
      gap: 6px;
      padding: 14px 16px;
      margin-bottom: 18px;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: var(--radius-md)
    }

    .client-profile-row {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 12px;
      font-size: 13px;
      color: var(--text2)
    }

    .client-profile-row strong {
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .client-profile-row .client-outstanding {
      color: var(--red-ink)
    }

    .client-profile-row .client-no-repeat-status {
      color: var(--red-ink)
    }

    /* A long reason wraps inside the drawer instead of widening it. */
    .client-profile-row .client-no-repeat-reason {
      flex: 1;
      min-width: 0;
      text-align: right;
      overflow-wrap: anywhere
    }

    .client-profile-actions {
      display: flex;
      justify-content: flex-end;
      flex-wrap: wrap;
      gap: 8px;
      margin-top: 4px
    }

    .client-profile-actions .btn {
      padding: 7px 12px;
      font-size: 12px
    }

    /* The inline reason editor for the no-repeat mark. The label sits above the field so a long
       reason has the full drawer width to read in. */
    .client-no-repeat-editor {
      display: flex;
      flex-direction: column;
      gap: 6px;
      margin-top: 6px;
      padding-top: 10px;
      border-top: 1px solid var(--border)
    }

    .client-no-repeat-label {
      font-size: 11.5px;
      font-weight: 600;
      color: var(--text3)
    }

    .client-no-repeat-input {
      width: 100%;
      box-sizing: border-box
    }

    /* The drawer headline carries the same mark as the name in the table. */
    .modal-title.is-no-repeat {
      color: var(--text3);
      text-decoration: line-through;
      text-decoration-thickness: 2px
    }

    .client-timeline {
      display: flex;
      flex-direction: column;
      gap: 10px
    }

    .client-entry {
      padding: 10px 12px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md)
    }

    .client-entry-top {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 10px;
      font-size: 11px;
      color: var(--text3)
    }

    .client-entry-section {
      font-weight: 600;
      letter-spacing: .04em;
      text-transform: uppercase;
      color: var(--text2)
    }

    .client-entry-subject {
      margin-top: 4px;
      font-size: 13px;
      font-weight: 500;
      color: var(--text)
    }

    .client-entry-figures {
      display: flex;
      flex-wrap: wrap;
      gap: 4px 12px;
      margin-top: 6px;
      font-size: 12px;
      color: var(--text2)
    }

    .client-entry-figures strong {
      color: var(--text);
      font-variant-numeric: tabular-nums
    }

    .client-entry-status {
      color: var(--text3)
    }

    .client-entry-empty {
      padding: 14px;
      font-size: 13px;
      color: var(--text3);
      text-align: center;
      background: var(--bg);
      border-radius: var(--radius-md)
    }

    /* ── Client drawer: the introduction tree ──
       Every client row records who introduced that client ("Refer by"), so one client's introducer
       can be read and their own introducer followed up the chain, while everyone the client brought
       in hangs below them. Every name is a link back into the drawer, so the tree can be walked in
       either direction, and a Class A introducer — someone who keeps sending business — is marked
       in the profile above.

       Each round down the tree carries its generation as a badge, so the depth of a whole branch
       reads at a glance: the client the drawer is open on is Gen 0, whoever they introduced is
       Gen 1, and each round below that steps up by one. The summary bar above the tree states the
       direct introductions, the size of the network and the deepest round it reaches. */

    .client-referral {
      display: flex;
      flex-direction: column;
      gap: 12px;
      padding: 12px 14px;
      margin-bottom: 18px;
      background: var(--bg);
      border: 1px solid var(--border);
      border-radius: var(--radius-md)
    }

    .client-refer-path,
    .client-refer-children {
      display: flex;
      flex-direction: column;
      gap: 6px
    }

    .client-refer-label {
      font-size: 11px;
      font-weight: 600;
      letter-spacing: .04em;
      text-transform: uppercase;
      color: var(--text3)
    }

    .client-refer-chain {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 6px;
      font-size: 13px;
      color: var(--text)
    }

    /* The head of the tree: the "Introduced" label on the left, the summary on the right. */
    .client-refer-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 10px;
      flex-wrap: wrap
    }

    .client-refer-stats {
      display: flex;
      align-items: baseline;
      gap: 10px;
      flex-wrap: wrap
    }

    .client-refer-stat {
      font-size: 11px;
      color: var(--text3)
    }

    .client-refer-stat strong {
      font-weight: 600;
      color: var(--text2)
    }

    .client-tree-node,
    .client-tree-name {
      cursor: pointer;
      color: var(--accent);
      border-bottom: 1px dashed transparent
    }

    .client-tree-node:hover,
    .client-tree-name:hover {
      border-bottom-color: currentColor
    }

    .client-tree-node.is-self {
      color: var(--text);
      font-weight: 600;
      cursor: default;
      border-bottom: 0
    }

    .client-tree-arrow {
      color: var(--text3)
    }

    .client-refer-empty {
      font-size: 12px;
      color: var(--text3)
    }

    .client-tree-list {
      list-style: none;
      margin: 0;
      padding-left: 14px;
      border-left: 1px dashed var(--border)
    }

    /* The root list holds the client the drawer is open on and nothing else, so it needs neither
       the indent nor the guide line the nested rounds are drawn with. */
    .client-tree-list.is-root {
      padding-left: 0;
      border-left: 0
    }

    .client-tree-item {
      position: relative;
      margin-top: 5px
    }

    /* The root is not a branch of anyone: no elbow, and no spacing taken from a round above it. */
    .client-tree-item.is-root {
      margin-top: 0
    }

    .client-tree-item.is-root::before {
      display: none
    }

    .client-tree-item::before {
      content: '';
      position: absolute;
      left: -14px;
      top: 9px;
      width: 10px;
      border-top: 1px dashed var(--border)
    }

    /* ── Generation badges: how far down the tree a name sits ──
       One badge per round, in the same shape for all of them, so it is the colour that carries the
       depth: quiet grey for the client's own round, then a step through the palette for each round
       below. The colours are the sheet's own status tints, so a badge reads as a state rather than
       as a new kind of decoration. */
    .client-gen-badge {
      display: inline-block;
      margin-left: 6px;
      padding: 1px 7px;
      border: 1px solid var(--border);
      border-radius: var(--radius-pill);
      background: var(--surface2);
      color: var(--text2);
      font-size: 10px;
      font-weight: 600;
      letter-spacing: .03em;
      white-space: nowrap;
      vertical-align: 1px;
      cursor: default
    }

    .client-gen-badge.is-g0 {
      background: var(--surface2);
      border-color: var(--border2);
      color: var(--text2)
    }

    .client-gen-badge.is-g1 {
      background: var(--accent-bg);
      border-color: rgba(0, 113, 227, .24);
      color: var(--accent)
    }

    .client-gen-badge.is-g2 {
      background: var(--green-bg);
      border-color: rgba(46, 125, 50, .22);
      color: var(--green-ink)
    }

    .client-gen-badge.is-g3 {
      background: var(--amber-bg);
      border-color: rgba(178, 106, 0, .22);
      color: var(--amber-ink)
    }

    .client-gen-badge.is-g4 {
      background: var(--sec-indigo-tint);
      border-color: rgba(94, 92, 230, .26);
      color: var(--sec-indigo)
    }

    .client-gen-badge.is-g5 {
      background: var(--sec-teal-tint);
      border-color: rgba(0, 194, 199, .32);
      color: #0f777a
    }

    .client-gen-badge.is-g6 {
      background: var(--red-bg);
      border-color: rgba(198, 40, 40, .22);
      color: var(--red-ink)
    }

    .client-class-a {
      color: var(--amber-ink)
    }

    /* ── Utility classes (replace repeated inline styles) ── */
    .u-mb0 { margin-bottom: 0 }
        .u-mt14 { margin-top: 14px }
    .u-mt28 { margin-top: 28px }
    .u-right { text-align: right }
    .u-hidden { display: none }
    .u-positive { color: var(--green-ink) }
        .u-meta { font-size: 16px; color: var(--text2) }
    .u-sub { font-size: 12px; color: var(--text3) }
    .u-prose { font-size: 13px; color: var(--text2); line-height: 1.6 }
    .u-note { font-size: 12px; color: var(--text2); margin-top: 10px }

    .u-medium { font-weight: 500 }
    .u-text { color: var(--text) }
    .u-hint { font-size: 11px; color: var(--text3) }
    .u-note-tight { font-size: 12px; color: var(--text2); margin-top: 4px }
    .u-block { display: block }
    .u-w-full { width: 100% }
    .row-total { background: var(--bg) }
    .cell-num-90 { width: 90px; text-align: right }
    .cell-num-120 { width: 120px; text-align: right }

    /* ══ SECTION A.2: INACTIVE TENANCIES (Self Manage / Break Lease / moved out / lapsed) ══
       A tenancy the owner has taken over, one that is breaking early, one whose tenant has moved
       out, or one whose Days expiry has gone negative (its period is over) is finished business.
       The row keeps every figure — the totals still have to add up — but it is washed out to a
       light grey so it reads as an entry that is no longer live. Nothing is struck through: the
       grey is the whole treatment, so the figures stay clean and readable. The tint is
       deliberately faint: at a glance the inactive rows recede, and without it they can still be
       read and edited. */

    .rev-row-closed td,
    .rev-row-no-repeat td {
      background: #f4f4f6;
      color: #8c8c8c
    }

    /* Grey is the whole treatment, so the row's green / amber tints and the left edge mark are
       removed rather than stacked on top of them. */
    .rev-row-closed td:first-child,
    .rev-row-no-repeat td:first-child {
      box-shadow: none
    }

    /* Hover stays inside the closed treatment — the row is not asking to be acted on. */
    .data-table tr.rev-row-closed:hover td,
    .data-table tr.rev-row-no-repeat:hover td {
      background: #eeeff2
    }

    /* The controls keep working but lose most of their contrast, so a closed row stops pulling
       the eye: the ×, the pickers and the drag handle all step back. The status column is left
       out of this group on purpose — its overlay must stay invisible and its dot keeps its
       colour (the two rules below), so :not() keeps both out.
       Without the :not(), this rule (two classes) would out-specify .rev-status-input (one
       class), lift the overlay to 50% opacity and reveal its box, arrow and selected text. */
    .rev-row-closed .delete-row-btn,
    .rev-row-no-repeat .delete-row-btn,
    .rev-row-closed .rev-select:not(.rev-status-input),
    .rev-row-no-repeat .rev-select:not(.rev-status-input),
    .rev-row-closed .rev-drag-handle,
    .rev-row-no-repeat .rev-drag-handle {
      opacity: .5;
      filter: grayscale(1)
    }

    /* The status overlay never shows itself: no box, arrow or text on a closed row, or any other.
       The selector is deeper than the row treatments above so neither their opacity nor their
       grayscale can bring it back. */
    .data-table .rev-status-cell .rev-status-input {
      opacity: 0;
      filter: none
    }

    /* The status dot is the one thing a greyed row does not wash out: the grey is about the
       figures, not the status, so the dot keeps its full colour and still reads at a glance. */
    .data-table tr.rev-row-closed .rev-status-cell .rev-status-badge,
    .data-table tr.rev-row-no-repeat .rev-status-cell .rev-status-badge {
      opacity: 1;
      filter: none
    }

    .rev-row-closed .delete-row-btn:hover,
    .rev-row-no-repeat .delete-row-btn:hover {
      background: transparent;
      color: var(--text3)
    }

    /* ══ RESIZABLE TABLE COLUMNS ══
       Every table gets a drag handle pinned to the right edge of each heading. At rest it draws one
       quiet hairline on that edge, so the columns are visible as columns; under the pointer the line
       steps up to the accent colour and doubles in weight, which is the whole invitation to drag.
       The 50px floor is a rule of the app, not a suggestion: a column can never be dragged narrower
       than that, whatever the drag asks for. */

    /* The rest line is not a third hairline for the design system — it is a column's own edge, so
       it is kept a step below the borders that shape cards and fields. In the light theme a .05 line
       on white is invisible at one pixel, and at night .05 of white disappears into the surface, so
       the two themes each get the weight that reads as "there, if you look". */
    :root {
      --col-divider: rgba(0, 0, 0, .10)
    }

    html[data-theme="dark"] {
      --col-divider: rgba(255, 255, 255, .14)
    }

    table thead th.col-resizable {
      position: relative
    }

    .col-resize-handle {
      position: absolute;
      top: 0;
      right: -4px;
      bottom: 0;
      width: 9px;
      cursor: col-resize;
      touch-action: none;
      user-select: none;
      z-index: 3
    }

    /* The last column's handle would hang four pixels past the table's right edge, which is
       enough to give the scroll container that holds the table a sliver of horizontal overflow
       (and so a scrollbar for a table that in fact fits). It sits inside the heading instead. */
    table thead th:last-child .col-resize-handle {
      right: 0
    }

    /* …and because of that shift, its lines sit flush on the table's own right edge rather than
       stopping four pixels short of the column boundary, where every other column's lines fall. */
    table thead th:last-child .col-resize-handle::before,
    table thead th:last-child .col-resize-handle::after {
      right: 0
    }

    /* Two lines share the edge: the quiet one that is always there, and the accent one the pointer
       brings. They cross-fade, so the edge brightens rather than moving. */
    .col-resize-handle::before,
    .col-resize-handle::after {
      content: '';
      position: absolute;
      border-radius: 2px;
      transition: opacity .12s ease-out, background .12s ease-out
    }

    .col-resize-handle::before {
      top: 3px;
      bottom: 3px;
      right: 4px;
      width: 1px;
      background: var(--col-divider)
    }

    .col-resize-handle::after {
      top: 2px;
      bottom: 2px;
      right: 4px;
      width: 2px;
      background: var(--accent);
      opacity: 0
    }

    .col-resize-handle:hover::before,
    table thead th.col-resizing .col-resize-handle::before {
      opacity: 0
    }

    .col-resize-handle:hover::after,
    table thead th.col-resizing .col-resize-handle::after {
      opacity: 1
    }

    /* While a drag is in flight the resize cursor follows the pointer everywhere and nothing on
       the page can be selected by accident. */
    body.col-resizing,
    body.col-resizing * {
      cursor: col-resize !important;
      user-select: none !important
    }

    /* The heading being dragged stays marked, so it is obvious which column is moving. */
    table thead th.col-resizing {
      color: var(--accent)
    }

    /* Once widths are applied, a cell's own minimum no longer gets to override the column: the
       dropdown columns are the ones that would otherwise refuse to go below their natural width. */
    table.col-widths-applied td.rev-select-cell {
      min-width: 0
    }

    /* The off-screen copy a column is measured against when its divider is double-clicked (see
       measureColumnContentWidth). Every value is forced onto one line and the caps that fold long
       text are lifted, so the width read back is the width the values actually need — with the
       cells' own padding and floors still counted, because the copy inherits them. */
    table.col-measuring th,
    table.col-measuring td,
    table.col-measuring th *,
    table.col-measuring td * {
      white-space: nowrap !important;
      overflow-wrap: normal !important;
      word-break: normal !important;
      max-width: none !important;
      text-overflow: clip !important
    }

    /* ══ 8. FLUID WIDE-SCREEN GRIDS FOR STACKED SECTIONS ══
       The goal is density: on a large or high-resolution display the sections of a tab sit
       two-up — the Networth tab's six asset sections as A | B, C | D, E | F, and the Revenue
       tab's four sheets as A.1 | A.2, B | C — so a tab is read across instead of down one long
       scroll. Instead of pinning one "magic" breakpoint, the layout scales with the
       monitor:
         · the shell is fluid, growing with the viewport up to a readable ceiling, so a
           full-HD, 1440p, 4K or Studio Display window all put their extra width to use
           rather than being held to a fixed 1200px column;
         · the section grid stays one column until two comfortable columns fit, then
           switches to two equal 1fr tracks that keep growing with the screen.
       A media query is read in CSS pixels, and CSS pixels already take the display's
       resolution and OS scaling into account — a Retina / HiDPI monitor reports fewer
       CSS pixels — so the same rule adapts to resolution for free. */

    /* Fluid shell: a 1200px floor for small laptops, ~94vw while the window still fits,
       and a 2400px ceiling so text lines never run uncomfortably long on a 5K panel. */
    .app-wrapper {
      max-width: clamp(1200px, 94vw, 2400px)
    }

    /* Sections A–F. Inert when there is no room: one column on a 28px rhythm. */
    .sections-grid {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      gap: 28px;
      margin-top: 28px;
      align-items: start
    }

    /* Inside the grid the gap owns the spacing, so the per-card divider margins are
       dropped and no card floats below its neighbour. Making every section a size
       container also lets the blocks inside it react to the section's own width rather
       than the viewport's — the trick that keeps the two-up layout from squeezing the
       denser strips once a section is only half the shell wide. One rule comes with
       containment: a floating card that is position: fixed and placed from pointer
       coordinates belongs at the root of the document rather than inside a section. */
    .sections-grid > .section-block {
      margin-top: 0;
      container-type: inline-size
    }

    /* Two up as soon as two comfortable columns fit (~1280px of usable width); below that
       a single column stays denser and easier to read. The 1fr tracks then scale the two
       columns with whatever width the monitor provides. */
    @media (min-width: 1280px) {
      .sections-grid:not(.sections-grid--revenue) {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }
    }

    /* Section C's ledger is designed at a 620px minimum. A two-up column can be narrower
       than that, so the table is allowed to compress instead of pushing a horizontal
       scrollbar into the card. */
    .sections-grid .investment-table {
      min-width: 0
    }

    /* The dense summary strips need real width, so at half a shell they pair up instead:
       Section B's four cash tiles and Section E's three-track property totals. */
    @media (min-width: 1280px) {
      @container (max-width: 900px) {
        .sections-grid .cash-grid.four,
        .sections-grid .property-total-grid {
          grid-template-columns: repeat(2, minmax(0, 1fr))
        }
      }
    }

    /* ── The Production tab's cards, on the same principle ──
       These are wide record tables rather than tiles, so the pairing waits until each half of the
       shell can hold a whole table: Section C's ten columns need ~900px, so the two columns only
       start at a ~2100px window. The breakpoint is measured, not guessed. Below it a single column
       stays the readable one — the two-up layout would otherwise put Section B and Section C's own
       columns behind a sideways scrollbar, which is exactly what the fitted widths exist to avoid.
       Either way a table wider than its card keeps its own sideways scroll rather than shrinking the
       page. Section A.1 and Section A.2 keep a full row each at every width — A.2 is the widest
       table in the app, so it is never squeezed into half a shell — and only Section B and Section C
       pair up. The tab opens on Section A.1, a plain card with no padding of its own, so the grid
       keeps its top margin to set the tab's opening. */
    @media (min-width: 2100px) {
      .sections-grid--revenue {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }

      /* Section A.2 carries the widest table in the app — Property, asset type, three date
         columns, tenancy status, TA format and rep — so it takes a whole row of its own directly
         under Section A.1 rather than sharing one. The two are no longer read side by side, and
         each table is read at the full width of the shell. */
      .sections-grid--revenue > .section--a1,
      .sections-grid--revenue > .section--a2 {
        grid-column: 1 / -1
      }

      /* The Net Income card closes the tab and holds the Year Summary inside it, and the
         three-ring Income Mix inside that, so it takes a whole row on its own rather than
         sharing one; Section B pairs with Section C above it. */
      .sections-grid--revenue > .section--rev {
        grid-column: 1 / -1
      }
    }

    /* ── The Production tab's client network ──
       The hotspot diagram that closes the Year Summary. It keeps one 960×420 rectangular frame
       (viewBox + preserveAspectRatio), so the old square's vertical space is no longer spent on
       empty corners. The frame grows to 920px and the filter rail sits beside it while the card
       can hold both; below that the rail drops under the drawing. The rectangle's colours are the
       Income Mix's own three, so a client's biggest stream reads without consulting the legend. */
    .client-network-bar {
      display: flex;
      align-items: center;
      gap: 12px;
      flex-wrap: wrap;
      margin-top: 14px
    }

    .client-network-hint {
      margin-top: 6px;
      font-size: 12px;
      color: var(--text3)
    }

    /* "All years" is two words, and a half-width card would otherwise break it across two lines. */
    .client-network-intro .section-date {
      white-space: nowrap
    }

    .client-network-search-wrap {
      flex: 1 1 220px;
      max-width: 320px
    }

    /* The diagram and the filter rail share one shell: the rail keeps a fixed 272px so the ranges
       do not jitter as labels change, and the rectangle takes the rest up to its 960px frame. The
       rail moves under the diagram once the card can no longer hold both side by side. */
    .client-network-shell {
      display: grid;
      grid-template-columns: minmax(0, 920px) 272px;
      justify-content: center;
      gap: 18px;
      align-items: start;
      margin-top: 12px
    }

    .client-network-main {
      min-width: 0
    }

    .client-network-shell .client-network-wrap {
      margin-top: 0
    }

    .cn-filter {
      padding: 14px 15px 16px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-md);
      box-shadow: var(--shadow-sm)
    }

    .cn-filter-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px
    }

    .cn-filter-title {
      font-size: 10.5px;
      font-weight: 700;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: var(--text2)
    }

    .cn-filter-reset {
      padding: 0;
      border: 0;
      background: transparent;
      color: var(--accent);
      font: inherit;
      font-size: 11.5px;
      font-weight: 600;
      cursor: pointer
    }

    .cn-filter-reset:hover {
      text-decoration: underline
    }

    .cn-filter-block {
      margin-top: 14px;
      padding-top: 12px;
      border-top: 1px solid var(--separator)
    }

    .cn-filter-block:first-of-type {
      margin-top: 12px;
      padding-top: 0;
      border-top: 0
    }

    .cn-filter-row {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 8px
    }

    .cn-filter-label {
      font-size: 11.5px;
      font-weight: 600;
      color: var(--text2)
    }

    .cn-filter-value {
      font-size: 10.5px;
      font-variant-numeric: tabular-nums;
      color: var(--text3);
      white-space: nowrap
    }

    .cn-filter-note {
      margin: 8px 0 0;
      font-size: 10.5px;
      line-height: 1.45;
      color: var(--text3)
    }

    .cn-type-grid {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: 6px;
      margin-top: 8px
    }

    .cn-type {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 7px 9px;
      border: 1px solid transparent;
      border-radius: 10px;
      background: var(--surface2);
      color: var(--text2);
      font: inherit;
      font-size: 11px;
      font-weight: 500;
      text-align: left;
      cursor: pointer;
      transition: var(--ease-out)
    }

    .cn-type:hover {
      color: var(--text);
      background: var(--border)
    }

    .cn-type.is-active {
      background: var(--text);
      color: var(--surface);
      font-weight: 600;
      box-shadow: 0 2px 10px rgba(0, 0, 0, .22)
    }

    .cn-type--all {
      grid-column: 1 / -1
    }

    .cn-type-dot {
      flex: 0 0 auto;
      width: 7px;
      height: 7px;
      border-radius: 50%
    }

    .cn-type.is-active .cn-type-dot {
      box-shadow: 0 0 0 2px rgba(255, 255, 255, .35)
    }
    /* A rail with one knob (the referral minimum) or two (year and commission). The outer element
       is the pointer surface, with 8px of slack so a knob at either end is not clipped; the inner
       element is the track the percentages are measured against, so 0% is the first dot and 100%
       the last. */
    .cn-range {
      position: relative;
      height: 34px;
      margin-top: 8px;
      padding: 0 8px;
      touch-action: pan-y;
      cursor: pointer
    }

    .cn-range-inner {
      position: relative;
      height: 100%
    }

    .cn-range-track,
    .cn-range-fill {
      position: absolute;
      top: 50%;
      height: 4px;
      margin-top: -2px;
      border-radius: 999px;
      pointer-events: none
    }

    .cn-range-track {
      left: 0;
      right: 0;
      background: var(--track)
    }

    .cn-range-fill {
      background: var(--accent)
    }

    .cn-range-dots {
      position: absolute;
      left: 0;
      right: 0;
      top: 50%;
      height: 10px;
      margin-top: -5px;
      pointer-events: none
    }

    .cn-range-dot {
      position: absolute;
      top: 50%;
      width: 4px;
      height: 4px;
      margin: -2px 0 0 -2px;
      border-radius: 50%;
      background: rgba(0, 0, 0, .22);
      transition: background .12s ease-out, transform .12s ease-out
    }

    .cn-range-dot.is-on {
      background: rgba(255, 255, 255, .9)
    }

    .cn-knob {
      position: absolute;
      top: 50%;
      width: 16px;
      height: 16px;
      margin: -8px 0 0 -8px;
      padding: 0;
      border: 1px solid var(--border2);
      border-radius: 50%;
      background: var(--surface);
      box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
      cursor: grab;
      touch-action: none;
      -webkit-appearance: none;
      appearance: none
    }

    /* A 16px knob is a small finger target; the transparent ring makes the hit area 28px without
       changing how the rail reads. */
    .cn-knob::before {
      content: '';
      position: absolute;
      inset: -6px;
      border-radius: 50%
    }

    .cn-knob:active {
      cursor: grabbing
    }

    .cn-knob:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px
    }

    .cn-range.is-disabled {
      opacity: .45;
      pointer-events: none
    }

    .cn-range-scale {
      display: flex;
      justify-content: space-between;
      margin: 2px 8px 0;
      font-size: 10px;
      font-variant-numeric: tabular-nums;
      color: var(--text3)
    }

    .cn-link-reset {
      padding: 0;
      border: 0;
      background: transparent;
      color: var(--accent);
      font: inherit;
      font-weight: 600;
      cursor: pointer
    }

    .cn-link-reset:hover {
      text-decoration: underline
    }
    /* A card too narrow for a side rail stacks it under the diagram. The rail then runs full
       width, so the type pills use three columns rather than two tall ones. */
    @container (max-width: 1230px) {
      .client-network-shell {
        grid-template-columns: minmax(0, 1fr)
      }

      .cn-filter {
        order: 0
      }

      .cn-type-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr))
      }

      .cn-type--all {
        grid-column: auto
      }
    }

    @container (max-width: 640px) {
      .cn-filter {
        padding: 12px 13px 14px
      }

      .cn-type-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }

      .cn-type--all {
        grid-column: 1 / -1
      }

      .cn-type {
        padding: 7px 8px;
        font-size: 10.5px
      }

      .cn-filter-value {
        font-size: 10px
      }

      .client-network-search-wrap {
        max-width: none
      }
    }





    .client-network-wrap {
      position: relative;
      width: 100%;
      max-width: 920px;
      margin: 12px auto 0;
      /* The diagram is a wide rectangle, not a square well: the boundary itself is drawn inside
         the SVG, so the wrapper stays transparent and simply centres it. */
      background: transparent;
      border-radius: 0;
      padding: 0;
      overflow: hidden
    }

    .client-network-svg {
      display: block;
      width: 100%;
      height: auto;
      touch-action: manipulation
    }

    .client-network-boundary {
      fill: var(--surface);
      stroke: var(--border2);
      stroke-width: 1
    }

    .client-network-svg.is-empty {
      display: none
    }

    /* Links are quiet until a circle is hovered or focused: at rest the picture reads as clusters,
       and the highlight brings one client's introductions forward without moving anything. */
    .client-net-edge {
      stroke: rgba(0, 0, 0, .16);
      fill: none;
      stroke-linecap: round;
      transition: stroke .12s ease-out, opacity .12s ease-out
    }

    /* When a filter is on, links between two non-matching context clients recede with them; a
       link touching a match keeps its normal weight so the matched branch stays readable. */
    .client-net-edge.is-context {
      opacity: .38
    }

    .client-net-edge.is-active {
      stroke: var(--sec-blue);
      stroke-opacity: .9
    }

    .client-net-edge.is-dimmed {
      opacity: .16
    }

    .client-net-node {
      cursor: pointer;
      outline: none
    }

    .client-net-node circle {
      stroke: var(--surface);
      stroke-width: 2;
      transition: opacity .12s ease-out, stroke .12s ease-out
    }

    /* The label is painted over a halo of the card's own colour, so a name crossing a link or a
       neighbouring circle still reads. */
    .client-net-node text {
      pointer-events: none;
      font-family: Inter, sans-serif;
      font-size: 10.5px;
      fill: var(--text);
      paint-order: stroke;
      stroke: var(--bg);
      stroke-width: 3px;
      stroke-linejoin: round
    }

    .client-net-node.is-dimmed {
      opacity: .2
    }

    .client-net-node.is-active circle {
      stroke: var(--text);
      stroke-width: 2.5
    }

    /* Every match keeps a dark ring while a filter is active, so the eye finds the clients that
       satisfy it even when the introducers beside them are drawn too. The accent blue would
       disappear into a sales circle, whose fill is the same blue. Everything else kept for context
       is faded. */
    .client-net-node.is-match circle {
      stroke: var(--text);
      stroke-width: 3.5
    }

    .client-net-node.is-context {
      opacity: .3;
      filter: grayscale(.7);
      transition: opacity .12s ease-out, filter .12s ease-out
    }

    .client-net-node.is-context:hover,
    .client-net-node.is-context:focus-visible {
      opacity: 1;
      filter: none
    }

    .client-net-node:focus-visible circle {
      stroke: var(--accent);
      stroke-width: 3
    }

    /* The SVG is drawn at 760 units wide and then scaled to the card, so a label that reads well at
       full width would be tiny in a phone-width card. The type is set in the frame's own units,
       which means it has to grow as the frame shrinks — hence the container, not the viewport: the
       Production tab's section is what the picture is squeezed into. */
    @container (max-width: 1000px) {
      .client-net-node text {
        font-size: 14px
      }
    }

    @container (max-width: 760px) {
      .client-net-node text {
        font-size: 17px
      }
    }

    /* On a phone the whole 960-unit frame cannot be squeezed into 400px without the labels
       disappearing, so below ~640px of card the diagram keeps a readable width of its own and the
       well scrolls sideways — the same choice the wide record tables make, rather than shrinking
       the page or the type. */
    @container (max-width: 640px) {
      .client-network-wrap {
        overflow-x: auto;
        overflow-y: hidden
      }

      .client-network-svg {
        min-width: 760px
      }
    }

    .client-network-empty {
      padding: 30px 20px;
      text-align: center;
      font-size: 13px;
      line-height: 1.6;
      color: var(--text3);
      max-width: 640px;
      margin: 0 auto
    }

    .client-network-empty strong {
      display: block;
      margin-bottom: 6px;
      font-size: 13.5px;
      color: var(--text)
    }

    .client-network-empty em,
    .client-link-editor-note em {
      font-style: normal;
      font-weight: 600;
      color: var(--text2)
    }

    /* The recorder sits in the card rather than in a modal: the diagram above it stays visible, so
       the line it is about to draw can be seen as the names are picked. */
    .client-link-editor {
      margin-top: 14px;
      padding: 14px 16px;
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: var(--radius-md)
    }

    .client-link-editor-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px
    }

    .client-link-editor-title {
      font-size: 13px;
      font-weight: 600;
      color: var(--text)
    }

    .client-link-editor-close {
      border: 0;
      background: transparent;
      color: var(--text3);
      font-size: 18px;
      line-height: 1;
      cursor: pointer;
      padding: 0 2px
    }

    .client-link-editor-close:hover {
      color: var(--text)
    }

    .client-link-editor-grid {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 12px;
      margin-top: 12px
    }

    @container (max-width: 620px) {
      .client-link-editor-grid {
        grid-template-columns: minmax(0, 1fr)
      }
    }

    .client-link-field {
      display: flex;
      flex-direction: column;
      gap: 6px;
      font-size: 12px
    }

    .client-link-field .form-input {
      width: 100%
    }

    .client-link-label {
      font-size: 11.5px;
      font-weight: 600;
      color: var(--text3)
    }

    .client-link-editor-note {
      margin-top: 10px;
      font-size: 11.5px;
      line-height: 1.55;
      color: var(--text3)
    }

    .client-link-editor .btn-row {
      margin-top: 12px
    }

    /* ── The Production tab's plan panel ──
       The tab opens on the four moves the week is meant to be run against, so the first
       thing on screen is the reminder rather than a record. It is a sibling of the card
       grid, never a member of it: it spans the whole shell instead of pairing up with a
       section, and the grid keeps its own top margin, which sets the distance down to
       Section A.1 below it.
       The panel is an ordinary card — same surface, hairline, radius and shadow — and the
       emphasis comes from the tab's own blue behind the head and from the type, not from a
       louder surface. The four moves themselves stay on plain white. */
    .income-plan {
      position: relative;
      padding: 26px 28px 24px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      overflow: hidden
    }

    /* A wash of the tab colour behind the head: enough to read as a cover sheet, faint
       enough to leave the moves below on white. */
    .income-plan::before {
      content: '';
      position: absolute;
      inset: 0 0 auto;
      height: 116px;
      background: linear-gradient(180deg, var(--sec-blue-tint), rgba(255, 255, 255, 0));
      pointer-events: none
    }

    /* The head repeats the section rail: 13px of padding behind a 3px accent bar. */
    .income-plan-head {
      position: relative;
      display: flex;
      align-items: flex-end;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 10px 24px;
      padding-left: 13px;
      border-left: 3px solid var(--sec-blue)
    }

    .income-plan-kicker {
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: var(--ls-label);
      text-transform: uppercase;
      color: var(--sec-blue)
    }

    .income-plan-title {
      margin-top: 3px;
      font-size: var(--fs-value);
      font-weight: 700;
      letter-spacing: -.02em;
      color: var(--text)
    }

    /* The one line that has to be read on the way back from the weekend, opposite the
       title. Accent on white rather than accent on its own tint — the tinted bed would
       drop it under a readable contrast at this size. */
    .income-plan-reminder {
      font-size: 13px;
      font-weight: 600;
      color: var(--accent);
      white-space: nowrap
    }

    /* The four moves: one row on a wide shell, two at half, one on a phone. The hairline
       above each draws a single rule across the row — which is what separates them
       without boxing every move into a second card. An ordered list, because the order
       is the point; the discs repeat the numbers the list already carries. */
    .income-plan-steps {
      position: relative;
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 20px;
      margin-top: 22px;
      list-style: none
    }

    .income-plan-step {
      display: flex;
      gap: 12px;
      padding-top: 16px;
      border-top: 1px solid var(--border)
    }

    .income-plan-no {
      flex: 0 0 auto;
      width: 26px;
      height: 26px;
      display: grid;
      place-items: center;
      border-radius: var(--radius-pill);
      background: var(--accent-bg);
      color: var(--accent);
      font-size: var(--fs-label);
      font-weight: 700;
      font-variant-numeric: tabular-nums
    }

    .income-plan-step-title {
      font-size: var(--fs-card-title);
      font-weight: 600;
      letter-spacing: -.01em;
      color: var(--text)
    }

    .income-plan-step-note {
      margin-top: 4px;
      font-size: var(--fs-sub);
      color: var(--text2)
    }

    @media(max-width:1180px) {

      .income-plan-steps {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }
    }

    @media(max-width:640px) {

      .income-plan {
        padding: 22px 20px 20px
      }

      .income-plan-steps {
        grid-template-columns: minmax(0, 1fr)
      }

      .income-plan-reminder {
        white-space: normal
      }
    }

    /* ── The three income engines ──
       The detail behind the plan above: what each engine is for, and the moves that feed it. Same
       card surface and the same head as the plan, so the two read as one pair — the emphasis is
       carried by the engine tags rather than by numbered discs. */
    .income-engines {
      margin-top: 20px;
      padding: 26px 28px 24px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm);
      overflow: hidden
    }

    .income-engines-grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 20px 24px;
      margin-top: 22px
    }

    /* One engine: a hairline across its top, the way the four moves above are ruled off. */
    .income-engine {
      padding-top: 16px;
      border-top: 1px solid var(--border)
    }

    .income-engine-head {
      display: flex;
      align-items: center;
      gap: 8px
    }

    .income-engine-tag {
      padding: 2px 9px;
      border-radius: var(--radius-pill);
      background: var(--accent-bg);
      color: var(--accent);
      font-size: var(--fs-label);
      font-weight: 700
    }

    .income-engine-kind {
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: var(--ls-label);
      color: var(--text3)
    }

    .income-engine-title {
      margin-top: 12px;
      font-size: var(--fs-value);
      font-weight: 700;
      letter-spacing: -.02em;
      color: var(--text)
    }

    .income-engine-goal {
      margin-top: 8px;
      font-size: var(--fs-sub);
      font-weight: 600;
      color: var(--text2)
    }

    /* The moves: a plain disc list, tight enough that four lines still sit inside the engine's own
       column rather than pushing that card taller than its neighbours. */
    .income-engine-list {
      display: grid;
      gap: 7px;
      margin-top: 12px;
      padding-left: 18px;
      font-size: var(--fs-sub);
      color: var(--text2)
    }

    .income-engine-list li::marker {
      color: var(--accent)
    }

    /* ── Linking the year tabs ──
       One switch above the section grid. Off (the default) each strip keeps its own year; on, a year
       picked anywhere in the tab is shown in every section. A real checkbox, hidden, with the track
       drawn around it, so it keeps its keyboard focus and its screen-reader role. */
    .rev-sync-bar {
      display: flex;
      align-items: center;
      gap: 14px;
      flex-wrap: wrap;
      margin-top: 20px;
      padding: 12px 18px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--shadow-sm)
    }

    .rev-sync {
      display: inline-flex;
      align-items: center;
      gap: 10px;
      cursor: pointer;
      user-select: none
    }

    .rev-sync input {
      position: absolute;
      width: 1px;
      height: 1px;
      opacity: 0
    }

    .rev-sync-track {
      position: relative;
      flex: 0 0 auto;
      width: 42px;
      height: 24px;
      border-radius: var(--radius-pill);
      background: var(--track);
      transition: background .2s ease-out
    }

    .rev-sync-knob {
      position: absolute;
      top: 3px;
      left: 3px;
      width: 18px;
      height: 18px;
      border-radius: 50%;
      background: #fff;
      box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
      transition: transform .2s ease-out
    }

    .rev-sync input:checked + .rev-sync-track {
      background: var(--sec-blue)
    }

    .rev-sync input:checked + .rev-sync-track .rev-sync-knob {
      transform: translateX(18px)
    }

    .rev-sync input:focus-visible + .rev-sync-track {
      outline: 2px solid var(--accent);
      outline-offset: 2px
    }

    .rev-sync-label {
      font-size: var(--fs-card-title);
      font-weight: 600;
      color: var(--text)
    }

    .rev-sync-hint {
      font-size: var(--fs-sub);
      color: var(--text3)
    }

    /* ── Search anything ──
       The search box at the end of the year-linking bar. Typing filters Sections A.1, A.2 and B down
       to the rows their unit (or client, or item) holds, and lights up every year tab that carries a
       match — so one query shows which year and which table a case lives in. Pushed to the right of
       the bar; it drops onto its own line once the bar wraps. */
    .rev-search-wrap {
      position: relative;
      display: inline-flex;
      align-items: center;
      flex: 1 1 240px;
      max-width: 340px;
      min-width: 168px;
      margin-left: auto
    }

    .rev-search-icon {
      position: absolute;
      left: 11px;
      font-size: 12px;
      line-height: 1;
      color: var(--text3);
      pointer-events: none
    }

    .rev-search {
      width: 100%;
      padding: 9px 12px 9px 31px;
      border: 1px solid var(--border);
      border-radius: var(--radius-pill);
      background: var(--surface2);
      color: var(--text);
      font: inherit;
      font-size: var(--fs-sub);
      font-variant-numeric: tabular-nums
    }

    .rev-search::placeholder {
      color: var(--text3)
    }

    .rev-search:focus {
      outline: 2px solid var(--accent);
      outline-offset: 1px;
      background: var(--surface)
    }

    /* ── The document builders beside the search ──
       LOI-Gen.html and TA-Gen.html are opened from the same bar as the search, so the two keep the
       search box's height and its capsule shape and sit together as one small group. A click opens
       the page in a window of its own (openDocGenerator, app.js); the dashboard is never navigated
       away from. */
    .rev-tools {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      flex: 0 0 auto;
      flex-wrap: wrap
    }

    .rev-tool-btn {
      padding: 8px 14px;
      font-size: var(--fs-sub);
      white-space: nowrap
    }

    /* ── The marks the search and the renewal calendar leave on a year tab ──
       A tab that holds a match reads in the section blue; when it is also the open tab it keeps its
       dark pill and takes a blue ring instead. A year whose rental sheet has a tenancy inside the
       90-day renewal window carries a small red dot, so a case sitting in another year is never a
       surprise. */
    .rev-year-tab {
      position: relative
    }

    .rev-year-tab.has-match {
      color: var(--sec-blue);
      font-weight: 600
    }

    .rev-year-tab.active.has-match {
      color: #fff;
      box-shadow: 0 0 0 2px var(--sec-blue), 0 2px 10px rgba(0, 0, 0, .22)
    }

    .rev-year-tab.has-renewal::after {
      content: '';
      position: absolute;
      top: 3px;
      right: 6px;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--red)
    }

    /* A year whose rental sheet holds a tenancy in the last days of its defect window (or past it)
       carries an amber dot of its own. When a year has both marks the red renewal dot sits just to
       the left of the amber one, so neither warning hides the other. */
    .rev-year-tab.has-defects::after {
      content: '';
      position: absolute;
      top: 3px;
      right: 6px;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--amber)
    }

    .rev-year-tab.has-renewal.has-defects::after {
      box-shadow: -6px 0 0 var(--red)
    }

    /* The muted line a searched table shows for a year with nothing left to draw. */
    .rev-search-empty td {
      padding: 15px 16px;
      text-align: center;
      color: var(--text3)
    }

    @media(max-width:1180px) {

      .income-engines-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr))
      }
    }

    @media(max-width:640px) {

      .income-engines {
        padding: 22px 20px 20px
      }

      .income-engines-grid {
        grid-template-columns: minmax(0, 1fr)
      }
    }

    /* ════════════════════════════════════════════════════════════════════
       9. APPLE HIG POLISH LAYER
       A second, later pass over the chrome so the app behaves like the system
       rather than like a web page: a floating vibrancy toolbar, an app-icon
       squircle, a real segmented control, capsule buttons, sheet edges, overlay
       scrollbars and system-grade motion. It touches presentation only — never a
       size a media query depends on — and it sits last, so where it speaks it wins.
       ════════════════════════════════════════════════════════════════════ */

    /* ── 9.1 Window chrome: a floating, translucent toolbar ──
       The header lifts out of the page into a vibrancy material, so content sliding
       underneath reads as depth — the same trick a Finder or Safari toolbar uses. */
    .app-header {
      position: sticky;
      top: 12px;
      z-index: 60;
      padding: 10px 14px;
      background: var(--material);
      -webkit-backdrop-filter: var(--blur);
      backdrop-filter: var(--blur);
      border: 1px solid var(--material-hairline);
      border-radius: var(--radius-lg);
      box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 10px 30px rgba(0, 0, 0, .07);
      transition: box-shadow var(--dur) var(--ease-apple)
    }

    /* Any engine without backdrop-filter gets an opaque bar instead of a see-through one. */
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
      .app-header {
        background: rgba(255, 255, 255, .97)
      }
    }

    /* ── 9.2 The app icon: an Apple squircle ──
       The HIG layer already chose a near-black tile — the Apple-logo trick of letting one
       neutral anchor the palette — so it stays dark, and only the drawing is polished: a
       continuous corner, a top inner highlight, a hairline contact shadow and a soft
       ambient one, so it reads as a system icon rather than a rounded square. */
    .app-logo {
      width: 44px;
      height: 44px;
      border-radius: 12px;
      background: linear-gradient(180deg, #3a3a3c 0%, #1d1d1f 58%, #141416 100%);
      box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .18),
        0 1px 1px rgba(0, 0, 0, .10),
        0 6px 16px rgba(0, 0, 0, .18)
    }

    .app-logo svg {
      width: 24px;
      height: 24px;
      stroke: #fff;
      filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .12))
    }

    /* ── 9.3 Segmented control (the dashboard tabs) ──
       Light track, white thumb with a system shadow, label darkening on selection: the
       iOS / macOS default appearance. The glide itself is unchanged — the indicator is
       still placed and sized from the script. */
    .dashboard-tabs {
      background: var(--track);
      border-radius: var(--radius-pill);
      box-shadow: inset 0 0 0 .5px rgba(0, 0, 0, .04)
    }

    .dashboard-tab-indicator {
      background: #fff;
      border-radius: var(--radius-pill);
      box-shadow:
        0 3px 8px rgba(0, 0, 0, .12),
        0 1px 1px rgba(0, 0, 0, .04),
        inset 0 0 0 .5px rgba(0, 0, 0, .04)
    }

    .dashboard-tab {
      color: var(--text2);
      font-weight: 500;
      transition: color .3s var(--ease-tab)
    }

    .dashboard-tab:hover {
      color: var(--text)
    }

    .dashboard-tab.active {
      color: var(--text);
      font-weight: 600
    }

    /* ── 9.4 Buttons: capsules with a real press state ──
       A scale on :active is what makes a tap feel physical; the fill steps through
       system blue's hover and pressed values instead of jumping to a darker shade. */
    .btn {
      border-radius: var(--radius-pill);
      font-weight: 500;
      letter-spacing: -.01em;
      -webkit-tap-highlight-color: transparent;
      transition: all .2s ease
    }

    .btn:active {
      transform: scale(.98)
    }

    .btn-primary {
      background: var(--accent);
      box-shadow: 0 1px 2px rgba(0, 0, 0, .10), 0 4px 12px rgba(0, 113, 227, .24)
    }

    .btn-primary:hover {
      background: var(--accent-strong)
    }

    .btn-primary:active {
      background: var(--accent-active)
    }

    .btn-ghost {
      background: var(--material-thick);
      border: 1px solid var(--border2);
      box-shadow: 0 1px 2px rgba(0, 0, 0, .04)
    }

    .btn-ghost:hover {
      background: #fff;
      border-color: var(--border2)
    }

    /* ── A table's add button: one plus in a circle ──
       The card above it already names what a new row is (a service record, a tenancy, a month), so
       the words were only repeating the heading. What is left is a single round target at the end of
       the strip; the words move to the tooltip and to the accessible name, so the button still reads
       for a screen reader and for a hover. Sized to the same height as the capsules it used to sit
       among, and held square so it stays a circle however the flex row grows. */
    .btn-add {
      width: 36px;
      height: 36px;
      min-width: 36px;
      padding: 0;
      border-radius: 50%;
      font-size: 19px;
      font-weight: 400;
      line-height: 1;
      flex: 0 0 auto
    }

    /* ── 9.5 Fields: system text fields ──
       Until now a .form-input had no base look at all and fell back to the browser's own
       control chrome. These are the system's: white fill, hairline edge, a faint inner
       shadow, and a soft blue halo on focus rather than a hard outline.
       The Excel-style editor inside a table cell is deliberately left alone: the in-table
       rule further up is more specific, so it still wins and the editor stays flush. */
    .cash-edit-input,
    .property-edit-input,
    .form-input {
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: var(--radius-sm);
      box-shadow: inset 0 1px 2px rgba(0, 0, 0, .03);
      color: var(--text);
      transition: border-color var(--dur-fast) var(--ease-apple), box-shadow var(--dur-fast) var(--ease-apple)
    }

    .cash-edit-input:focus,
    .property-edit-input:focus,
    .form-input:focus,
    .edit-cell-input:focus {
      border-color: var(--accent);
      box-shadow: var(--focus-ring);
      outline: none
    }

    /* ── 9.6 Surfaces: one card, one shadow, one hover ──
       Hover lifts by a single pixel and leans on the shadow, which is how the system
       signals "this is tappable" without the card jumping off the page. */
    .card:hover,
    .property-card:hover,
    .cash-item:not(.is-readonly):hover,
    .cash-total.is-clickable:hover {
      transform: translateY(-1px)
    }

    .data-table tbody tr:hover,
    .investment-table tbody tr:hover {
      background: var(--surface3)
    }

    /* Figures inside a monospaced cell use the system's own mono face. */
    .data-table td.mono {
      font-family: var(--font-mono)
    }

    /* ── 9.7 Sheets: the side panel gets Apple's rounded leading edge ──
       The panel floats over a blurred, dimmed page; its exposed edge is rounded and the
       material is thick enough that long tables stay comfortable to read. */
    .modal {
      border-radius: var(--radius-xl) 0 0 var(--radius-xl);
      border-left: 1px solid var(--border);
      background: var(--material-thick);
      -webkit-backdrop-filter: var(--blur);
      backdrop-filter: var(--blur)
    }

    .modal-header {
      background: transparent;
      border-bottom: 1px solid var(--separator)
    }

    .modal-backdrop {
      background: rgba(0, 0, 0, .32);
      -webkit-backdrop-filter: saturate(180%) blur(24px);
      backdrop-filter: saturate(180%) blur(24px)
    }

    /* On a phone the sheet is the whole screen, so it squares off again. */
    @media (max-width: 744px) {
      .modal {
        border-radius: 0;
        border-left: 0
      }
    }

    /* ── 9.8 Overlay scrollbars ──
       Thin and rounded, drawn inside a transparent gutter so the thumb appears to float.
       The always-on horizontal bar under a wide table keeps its own 12px size: a class
       selector outbids a bare ::-webkit-scrollbar, so rule 4 still wins where it matters. */
    ::-webkit-scrollbar {
      width: 10px;
      height: 10px
    }

    ::-webkit-scrollbar-track,
    ::-webkit-scrollbar-corner {
      background: transparent
    }

    ::-webkit-scrollbar-thumb {
      background: var(--scroll-overlay);
      background-clip: content-box;
      border: 3px solid transparent;
      border-radius: var(--radius-pill)
    }

    ::-webkit-scrollbar-thumb:hover {
      background: rgba(0, 0, 0, .45);
      background-clip: content-box
    }

    /* ── 9.9 Focus and motion, the system way ──
       Motion is decoration: when the system asks for less, the app stops moving. */
    @media (prefers-reduced-motion: reduce) {

      *,
      *::before,
      *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important
      }
    }


    /* ════════════════════════════════════════════════════════════════════
       10. DESIGN-SPEC LOCK
       §9 drew the system's chrome; this pass only pins the numbers. The page
       grey, the white card and its hairline, the two greys of type, system blue
       and its hover, one wide 4%-opacity shadow, the enlarged corner radius and
       the capsule press. It is appended after §9, so where the two disagree this
       one wins — and it still never touches a size a media query depends on.
       ════════════════════════════════════════════════════════════════════ */

    /* ── 10.1 One radius for every card-level surface ──
       §7 already lifted the cards, but the cash tiles, the metrics bar, the status
       banner and the section blocks were still on the small tile radius. They are
       siblings of a card rather than children of one, so they take the card radius
       too. Wells *inside* a card (a summary strip, a callout) stay one step down at
       14px: an inner corner is always smaller than the corner containing it, or the
       nesting reads heavy. */
    .card,
    .property-card,
    .cash-item,
    .cash-total,
    .status-banner,
    .metrics-bar,
    .chart-wrap,
    .edit-table-wrap,
    .investment-table-wrap,
    .section-block {
      border-radius: var(--radius-lg)
    }

    /* ── 10.2 Room to breathe ──
       Every gap between surfaces grows by a step, so the page is separated by air
       rather than by lines. Only the grid gaps and paddings move; no width, no
       breakpoint and no track size is touched. */
    .app-wrapper {
      padding: 40px 32px 104px
    }

    .cards-grid,
    .property-grid {
      gap: 20px;
      margin-bottom: 28px
    }

    .sections-grid {
      gap: 32px;
      margin-top: 32px
    }

    .metric-badge {
      margin-top: 4px
    }

    /* ── 10.3 The display figure ──
       A hero number is heavier, tighter and larger than its label, and set on the tabular
       grid so a column of figures lines up on the decimal. */
    .card-value,
    .cash-total-value,
    .cash-item-value,
    .summary-card-value {
      font-weight: 700;
      letter-spacing: -.5px;
      font-variant-numeric: tabular-nums
    }

    /* The four headline KPI cards take the hero step at 900px and above — one size below
       the net-worth figure, so the two headline blocks never compete for the same glance.
       The blanket "card values" group in §1 had swept .card-value down to the 20px body
       step; this restores the enlarged figure the card was designed around. */
    .card-value {
      font-size: var(--fs-hero-sm);
      line-height: 1.05
    }

    @media (min-width: 900px) {

      .card-value {
        font-size: var(--fs-hero)
      }
    }

    /* ── One figure size in Net Worth ──
       The SRS cards, the CPF cards and the return-metric strip were the only places in the tab
       that stepped up to the hero figure — four 32px numbers in a row under a 28px headline —
       while every other total is set at the value size. They now all read at that size, so the
       tab carries one figure size for its numbers instead of three. */
    #tab-networth .card-value,
    #tab-networth .card-dual strong,
    #tab-networth .metric-value {
      font-size: var(--fs-value-sm);
      letter-spacing: -.01em;
      line-height: 1.25
    }

    /* ── 10.4 Chart labels take the page's own face ──
       The donut and line charts carry their label face in the markup as a
       font-family attribute, and a presentation attribute loses to any CSS rule.
       This one selector re-faces every chart label in the document — they stop
       asking for Inter, which was never loaded, and start matching the page. */
    svg text[font-family*="Inter"] {
      font-family: var(--font-sans)
    }

    /* ── 10.5 Badges and chips ──
       A badge is always a full capsule, and its bed is a soft pastel rather than a
       translucent wash, so it stays soft even where two of them sit side by side. */
    .card-pill,
    .card-tag,
    .year-badge,
    .chip {
      border-radius: var(--radius-pill)
    }

    /* ════════════════════════════════════════════════════════════════════
       11. DARK APPEARANCE · component corrections
       ────────────────────────────────────────────────────────────────────
       The token block near the top re-points the design system. A handful of
       pieces were drawn with literal light colours before those tokens
       existed — a white tab pill, pastel chips, a pale row wash, a grey chart
       track — and those literals do not follow a variable. This layer is the
       one place they are answered: every rule below is scoped to the dark
       attribute, so light mode stays exactly as it was, and because it sits at
       the end of the sheet it wins over the original literal without needing
       to rewrite each component in place.
       ════════════════════════════════════════════════════════════════════ */

    /* ── Window chrome and page surfaces ── */
    html[data-theme="dark"] .app-header {
      box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 10px 30px rgba(0, 0, 0, .40)
    }

    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
      html[data-theme="dark"] .app-header {
        background: rgba(24, 24, 27, .97)
      }
    }

    /* The section card is a gradient in both modes; only its base needs a dark fill beneath. */
    html[data-theme="dark"] .section-block {
      background-color: var(--surface);
      background-image: linear-gradient(180deg, var(--section-tint), rgba(0, 0, 0, 0) 190px)
    }

    html[data-theme="dark"] .tab-empty {
      background: rgba(255, 255, 255, .03)
    }

    /* ── Segmented controls: the chosen stop is a raised graphite pill, not a white one ── */
    html[data-theme="dark"] .dashboard-tabs {
      box-shadow: inset 0 0 0 .5px var(--border)
    }

    html[data-theme="dark"] .dashboard-tab-indicator {
      background: #48484a;
      box-shadow:
        0 3px 8px rgba(0, 0, 0, .45),
        0 1px 1px rgba(0, 0, 0, .30),
        inset 0 0 0 .5px rgba(255, 255, 255, .10)
    }

    html[data-theme="dark"] .dashboard-tab.active {
      color: #fff
    }

    html[data-theme="dark"] .rev-year-tabs {
      box-shadow: inset 0 1px 2px rgba(0, 0, 0, .35)
    }

    html[data-theme="dark"] .rev-year-tab.active,
    html[data-theme="dark"] .edit-tab.active {
      background: #48484a;
      color: #fff;
      border-color: #5a5a5e;
      box-shadow: 0 2px 10px rgba(0, 0, 0, .45), inset 0 0 0 .5px rgba(255, 255, 255, .08)
    }

    html[data-theme="dark"] .theme-option.active {
      background: #48484a;
      color: #fff;
      box-shadow: 0 1px 3px rgba(0, 0, 0, .45), inset 0 0 0 .5px rgba(255, 255, 255, .10)
    }

    /* ── Buttons ── */
    html[data-theme="dark"] .btn-ghost:hover {
      background: var(--surface2);
      border-color: var(--border2)
    }

    html[data-theme="dark"] .btn-danger:hover {
      background: rgba(255, 69, 58, .20)
    }

    /* ── Status beds and chip pastels: the same hue, read as a wash on a dark card ── */
    html[data-theme="dark"] .chip.reit {
      background: rgba(10, 132, 255, .18);
      color: #7bb8ff
    }

    html[data-theme="dark"] .chip.bank {
      background: rgba(0, 194, 199, .18);
      color: #63e2e6
    }

    html[data-theme="dark"] .chip.etf {
      background: rgba(48, 209, 88, .16);
      color: #74e78f
    }

    html[data-theme="dark"] .chip.bond {
      background: rgba(255, 159, 10, .16);
      color: #ffc061
    }

    html[data-theme="dark"] .chip.tbill {
      background: rgba(191, 90, 242, .18);
      color: #d9a6ff
    }

    html[data-theme="dark"] .net-worth-grid .net-worth-result {
      background: rgba(10, 132, 255, .12);
      border-color: rgba(10, 132, 255, .28)
    }

    /* ── Production tables: the row washes that were mixed against white ── */
    html[data-theme="dark"] .rev-row-closed td,
    html[data-theme="dark"] .rev-row-no-repeat td {
      background: #1d1d21;
      color: #8e8e93
    }

    html[data-theme="dark"] .data-table tr.rev-row-closed:hover td,
    html[data-theme="dark"] .data-table tr.rev-row-no-repeat:hover td {
      background: #242429
    }

    html[data-theme="dark"] .data-table tr.rev-row-upcoming:hover td {
      background: rgba(48, 209, 88, .24)
    }

    html[data-theme="dark"] #tab-revenue .data-table td.rev-linked-cell {
      background: rgba(255, 255, 255, .04)
    }

    html[data-theme="dark"] .rev-row-working td,
    html[data-theme="dark"] .rev-row-active td {
      background: rgba(255, 199, 0, .14)
    }

    html[data-theme="dark"] .data-table tr.rev-row-working:hover td,
    html[data-theme="dark"] .data-table tr.rev-row-active:hover td {
      background: rgba(255, 199, 0, .22)
    }

    /* ── Floating cards: the remarks bubble keeps its inverted reading, on a dark surface ── */
    html[data-theme="dark"] .rev-remarks-popover {
      background: var(--surface2);
      color: var(--text);
      border: 1px solid var(--border2);
      box-shadow: var(--shadow-md)
    }

    /* ── Client network: links were drawn with a black hairline against a white card ── */
    html[data-theme="dark"] .client-net-edge {
      stroke: rgba(255, 255, 255, .18)
    }

    html[data-theme="dark"] .cn-range-dot {
      background: rgba(255, 255, 255, .28)
    }

    /* ── Charts: the SVG parts that were given literal light inks in the markup ──
       A presentation attribute always loses to a CSS rule, so these selectors re-ink the
       donut tracks, the labels and the grid lines without touching the drawing code. */
    html[data-theme="dark"] svg circle[stroke="#e5e5e5"] {
      stroke: rgba(255, 255, 255, .09)
    }

    html[data-theme="dark"] svg text[fill="#1d1d1f"] {
      fill: var(--text)
    }

    html[data-theme="dark"] svg text[fill="#86868B"],
    html[data-theme="dark"] svg text[fill="#86868b"] {
      fill: var(--text2)
    }

    html[data-theme="dark"] svg [stroke^="rgba(0,0,0"],
    html[data-theme="dark"] svg [stroke^="rgba(0, 0, 0"] {
      stroke: rgba(255, 255, 255, .12)
    }

    html[data-theme="dark"] svg [fill^="rgba(0,0,0"],
    html[data-theme="dark"] svg [fill^="rgba(0, 0, 0"] {
      fill: rgba(255, 255, 255, .42)
    }

    /* ── Scrollbars: the hover thumb needs to lighten, not darken, on a dark page ── */
    html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
      background: rgba(255, 255, 255, .45)
    }

    /* ── A phone keeps the switch, but drops the words to two icons ── */
    @media(max-width:640px) {
      .theme-option {
        padding: 7px 9px
      }

      .theme-option span {
        display: none
      }
    }

/* ═══════════════════════════════════════════════════════════════════════
   TAB: MEDICAL
   A visit, a result and an appointment are the same row, so the history is
   one table read newest-first and the future simply sits at the top. Rose
   is the section's accent; the amber bed marks what a reminder says is due.
   Every colour here is a token, so the dark appearance needs no second set.
   ═══════════════════════════════════════════════════════════════════════ */
.section--med {
  --section-accent: var(--sec-pink);
  --section-tint: var(--sec-pink-tint)
}

.med-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0
}

.med-block {
  margin-top: 30px
}

.med-block-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 12px
}

.med-count {
  color: var(--text3);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0
}

.med-filters {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap
}

.med-search {
  width: 230px
}

.med-select {
  width: auto;
  min-width: 132px;
  max-width: 210px
}

.med-link-btn {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  cursor: pointer
}

.med-link-btn:hover {
  color: var(--accent-strong);
  text-decoration: underline
}

.med-mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px
}

/* ── The four numbers ── */
.med-stats .med-stat-attention .cash-item-value {
  color: var(--amber-ink)
}

/* ── Recurring care ──
   One card per rule, urgent first. The card is not a button: the date is
   read, then "Mark done" writes a record like any other. */
.med-care {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: 10px
}

.med-care-card {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 13px 15px 11px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface)
}

.med-care-card.is-due {
  border-color: rgba(255, 149, 0, .38);
  background: var(--amber-bg)
}

.med-care-card.is-never {
  border-style: dashed
}

.med-care-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px
}

.med-care-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text)
}

.med-cadence {
  font-size: 11px;
  color: var(--text3);
  white-space: nowrap
}

.med-care-state {
  font-size: 12px;
  font-weight: 600;
  color: var(--text2)
}

.med-care-state.is-due {
  color: var(--amber-ink)
}

.med-care-state.is-soon {
  color: var(--amber-ink)
}

.med-care-state.is-booked,
.med-care-state.is-ok {
  color: var(--green-ink)
}

.med-care-state.is-never {
  color: var(--text3)
}

.med-care-meta {
  font-size: 11.5px;
  color: var(--text2)
}

.med-care-meta strong {
  color: var(--text);
  font-weight: 600
}

.med-care-note {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text3)
}

.med-care-actions {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 2px
}

.med-dot {
  color: var(--text3);
  font-size: 11px
}

.med-empty-card {
  padding: 18px 16px;
  border: 1px dashed var(--border2);
  border-radius: 14px;
  color: var(--text3);
  font-size: 12.5px;
  line-height: 1.5
}

/* ── The history table ──
   One row per record, the date in its own column so the eye can run down it,
   and long text clamped: a row stays a row, the whole entry is one click away. */
.med-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch
}

.med-table {
  min-width: 820px
}

.med-table th {
  white-space: nowrap
}

.med-table td {
  vertical-align: top;
  padding-top: 13px;
  padding-bottom: 13px
}

.med-table tbody tr.med-row {
  cursor: pointer;
  transition: background .14s ease
}

.med-table tbody tr.med-row:hover {
  background: var(--surface3)
}

.med-table tbody tr.med-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px
}

.med-table tbody tr.is-due,
.med-table tbody tr.is-soon {
  background: var(--amber-bg);
  box-shadow: inset 3px 0 0 var(--amber)
}

.med-table tbody tr.is-due:hover,
.med-table tbody tr.is-soon:hover {
  background: var(--amber-bg);
  filter: brightness(.985)
}

.med-table tbody tr.is-future .med-date {
  color: var(--accent)
}

.med-year-row td {
  padding: 16px 0 6px;
  background: transparent
}

.med-year-count {
  margin-left: 8px;
  font-size: 11px;
  color: var(--text3)
}

.med-actions-head {
  width: 40px
}

.med-date-cell {
  white-space: nowrap
}

.med-date {
  font-size: 13px;
  font-weight: 600;
  color: var(--text)
}

.med-place {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--text3)
}

.med-purpose {
  font-weight: 600;
  color: var(--text)
}

.med-cond-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 5px
}

.med-cond-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--surface2);
  color: var(--text2);
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.6;
  white-space: nowrap
}

.med-due-chip {
  display: inline-block;
  margin-top: 5px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
  background: var(--amber-bg);
  color: var(--amber-ink);
  box-shadow: inset 0 0 0 .5px rgba(255, 149, 0, .35)
}

.med-due-chip.is-booked {
  background: var(--accent-bg);
  color: var(--accent);
  box-shadow: inset 0 0 0 .5px rgba(0, 113, 227, .25)
}

.med-result-text,
.med-follow-text {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text2)
}

.med-result-text.is-clamped {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden
}

.med-follow-text {
  -webkit-line-clamp: 2
}

.med-cost-cell {
  white-space: nowrap
}

.med-money {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text)
}

.med-insured {
  display: inline-block;
  margin-top: 3px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--green-bg);
  color: var(--green-ink);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap
}

.med-row-actions {
  text-align: right;
  white-space: nowrap
}

.med-table .delete-row-btn {
  opacity: 0
}

.med-table tbody tr:hover .delete-row-btn,
.med-table .delete-row-btn:focus-visible {
  opacity: 1
}

.med-empty {
  color: var(--text3);
  font-size: 12.5px
}

.med-empty-row td {
  padding: 22px 0;
  text-align: center;
  color: var(--text3)
}

/* ── Conditions & progression ──
   Two columns of cards while the page can hold them; each card is a headline,
   a status and the timeline, so "is this getting better?" is answered by
   reading downward without opening a record. */
.med-conditions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
  gap: 12px
}

.med-condition {
  padding: 16px 18px 14px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--surface);
  box-shadow: inset 3px 0 0 var(--med-tone, var(--sec-slate))
}

.med-condition.is-routine {
  --med-tone: var(--green)
}

.med-condition.is-watch {
  --med-tone: var(--amber)
}

.med-condition.is-concern {
  --med-tone: var(--red)
}

.med-condition-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px
}

.med-condition-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text)
}

.med-condition-when {
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--text3)
}

.med-condition-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0
}

.med-status {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.7;
  white-space: nowrap;
  background: var(--surface2);
  color: var(--text2)
}

.med-status.is-routine {
  background: var(--green-bg);
  color: var(--green-ink)
}

.med-status.is-watch {
  background: var(--amber-bg);
  color: var(--amber-ink)
}

.med-status.is-concern {
  background: var(--red-bg);
  color: var(--red-ink)
}

.med-condition-summary {
  margin: 10px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text2)
}

.med-icon-btn {
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text3);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: background .14s ease, color .14s ease
}

.med-icon-btn:hover {
  background: var(--surface2);
  color: var(--text)
}

.med-timeline {
  list-style: none;
  margin: 14px 0 0;
  padding: 0
}

.med-tl {
  position: relative;
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: 12px;
  padding: 0 0 12px 14px
}

.med-tl::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--med-tone, var(--sec-slate));
  box-shadow: 0 0 0 3px var(--surface)
}

.med-tl::after {
  content: "";
  position: absolute;
  left: 3px;
  top: 14px;
  bottom: 0;
  width: 1px;
  background: var(--border2)
}

.med-tl:last-child {
  padding-bottom: 0
}

.med-tl:last-child::after {
  display: none
}

.med-tl.is-future::before {
  background: var(--accent)
}

.med-tl-date {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text2);
  white-space: nowrap
}

.med-tl-title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text)
}

.med-tl-place {
  margin-top: 1px;
  font-size: 11px;
  color: var(--text3)
}

.med-tl-result {
  margin-top: 3px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text2)
}

.med-tl-follow {
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--amber-ink)
}

/* ── The three drawers ──
   The .modal drawer is shared, so only the three things the medical editors
   add are styled here: the checkbox rail, the hint under it, and the footer. */
.med-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 2px 0 14px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--text2);
  cursor: pointer
}

.med-check input {
  margin: 2px 0 0;
  accent-color: var(--accent)
}

.med-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-bottom: 6px
}

.med-check-inline {
  margin: 0
}

.med-hint {
  margin-bottom: 14px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text3)
}

.med-textarea {
  min-height: 0;
  resize: vertical;
  line-height: 1.5
}

.med-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 6px;
  padding-top: 14px;
  border-top: 1px solid var(--separator)
}

/* ── Narrower screens ──
   The table keeps its columns and scrolls; the cards drop to one across. */
@media(max-width:1080px) {
  .med-conditions {
    grid-template-columns: 1fr
  }
}

@media(max-width:860px) {
  .med-toolbar {
    flex-wrap: wrap
  }

  .med-search {
    width: 100%
  }

  .med-filters {
    width: 100%
  }

  .med-select {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none
  }

  .med-tl {
    grid-template-columns: 78px 1fr
  }
}

/* ── Dark appearance ──
   The tokens already flip; the two tints that were mixed as rgba need the
   amber edge to stay visible on a dark card. */
html[data-theme="dark"] .med-care-card.is-due {
  border-color: rgba(255, 159, 10, .45)
}

html[data-theme="dark"] .med-due-chip {
  box-shadow: inset 0 0 0 .5px rgba(255, 159, 10, .4)
}


