  /* ---------- Fonts (self-hosted) ----------
     Served from this folder rather than fonts.googleapis.com: a request to Google would
     hand a third party the visitor IP and the fact that they use Daybook, which is exactly
     what this app promises not to do. It also means the page works with no network at all.
     Fraunces, Inter and IBM Plex Mono are all SIL Open Font License 1.1 — see fonts/LICENSE.
     Only the latin and latin-ext subsets are shipped; unicode-range means a browser fetches
     just the ones it needs. */
  @font-face{
    font-family:"Fraunces";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/fraunces-600-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"Fraunces";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/fraunces-600-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-400-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-400-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:500;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-500-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:500;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-500-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-600-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"IBM Plex Mono";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/ibm-plex-mono-600-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src:url("fonts/inter-400-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src:url("fonts/inter-400-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:500;
    font-display:swap;
    src:url("fonts/inter-500-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:500;
    font-display:swap;
    src:url("fonts/inter-500-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/inter-600-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:600;
    font-display:swap;
    src:url("fonts/inter-600-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:700;
    font-display:swap;
    src:url("fonts/inter-700-latin-ext.woff2") format("woff2");
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  @font-face{
    font-family:"Inter";
    font-style:normal;
    font-weight:700;
    font-display:swap;
    src:url("fonts/inter-700-latin.woff2") format("woff2");
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }

  :root{
    --paper:#F6F1E6;
    --paper-2:#EFE8D8;
    --ink:#1E2A2E;
    --ink-soft:#5C6864;
    --rule:#D9CFB8;
    --rule-strong:#B9AD8E;
    --teal:#2F6F5E;
    --teal-soft:#DCE9E3;
    --rust:#B3492E;
    --rust-soft:#F3DFD6;
    --gold:#C08A28;
    /* The tint that goes with --gold, for the backup reminder and the shadowed-rule warning —
       the same relationship --teal-soft and --rust-soft have to their own hues. */
    --gold-soft:#F7ECD3;
    --white:#FFFDF8;
    /* Chart series. Siblings of --teal/--rust, stepped up in chroma so the pair clears the
       colorblind-separation and chroma floors against the --white chart surface. */
    --chart-in:#00806A;
    --chart-out:#B3492E;
    --chart-last:#D8B08C;
    --shadow: 0 1px 0 rgba(30,42,46,0.06), 0 8px 24px -12px rgba(30,42,46,0.18);
  }
  *{box-sizing:border-box;}
  /* The UA's own [hidden] rule is a bare attribute selector, so any `display` this stylesheet
     sets on an element by id or class silently outranks it and the element stays on screen. */
  [hidden]{display:none !important;}
  html,body{margin:0;padding:0;}
  body{
    background:
      repeating-linear-gradient(180deg, transparent, transparent 34px, rgba(185,173,142,0.14) 34px, rgba(185,173,142,0.14) 35px),
      var(--paper);
    color:var(--ink);
    font-family:'Inter', sans-serif;
    -webkit-font-smoothing:antialiased;
    min-height:100vh;
    padding-bottom:80px;
  }
  .wrap{max-width:1040px;margin:0 auto;padding:0 24px;}

  /* ---------- Header ribbon ---------- */
  .ribbon{
    background:var(--ink);
    color:var(--paper);
    padding:28px 0 22px;
    position:relative;
    overflow:hidden;
  }
  .ribbon::after{
    content:"";
    position:absolute;
    left:0;right:0;bottom:0;
    height:6px;
    background:repeating-linear-gradient(90deg, var(--paper) 0 10px, transparent 10px 20px);
    opacity:0.9;
  }
  .ribbon-inner{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;flex-wrap:wrap;}
  .brand-eyebrow{
    font-family:'IBM Plex Mono', monospace;
    font-size:11px;
    letter-spacing:0.16em;
    text-transform:uppercase;
    color:var(--gold);
    margin:0 0 6px;
  }
  .brand-title{
    font-family:'Fraunces', serif;
    font-weight:600;
    font-size:42px;
    line-height:1;
    margin:0;
    letter-spacing:-0.01em;
  }
  .brand-logo{
    display:inline-block;
    margin-right:10px;
    font-size:0.85em;
    vertical-align:-0.08em;
  }
  .brand-sub{
    font-size:13.5px;
    color:#C9CFC9;
    margin:8px 0 0;
    max-width:420px;
  }

  /* ---------- Sections ---------- */
  /* Every section pads both edges, so this is half the gap between two of them. */
  .section{padding:14px 0;}
  .section-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:14px;flex-wrap:wrap;gap:8px;}
  .section-title{
    font-family:'Fraunces', serif;
    font-size:20px;
    font-weight:600;
    margin:0;
  }
  .section-hint{font-size:12.5px;color:var(--ink-soft);}

  /* ---------- Dropzone ---------- */
  .dropzone{
    border:1.5px dashed var(--rule-strong);
    border-radius:4px;
    background:var(--white);
    padding:30px 20px;
    text-align:center;
    cursor:pointer;
    transition:border-color .15s ease, background .15s ease;
  }
  .dropzone:hover, .dropzone.drag{
    border-color:var(--teal);
    background:var(--teal-soft);
  }
  .dropzone-title{font-weight:600;font-size:15px;margin:0 0 4px;}
  .dropzone-sub{font-size:12.5px;color:var(--ink-soft);margin:0;}
  .dropzone input{display:none;}

  .btn{
    font-family:'Inter', sans-serif;
    font-size:13px;
    font-weight:600;
    padding:9px 16px;
    border-radius:3px;
    border:1px solid var(--ink);
    background:var(--ink);
    color:var(--paper);
    cursor:pointer;
    transition:opacity .12s ease;
  }
  .btn:hover{opacity:0.85;}
  .btn.ghost{background:transparent;color:var(--ink);border:1px solid var(--rule-strong);}
  .btn.ghost:hover{background:var(--paper-2);opacity:1;}
  .btn.small{padding:6px 11px;font-size:12px;}
  .btn.danger{border-color:var(--rust);color:var(--rust);background:transparent;}
  .btn.danger:hover{background:var(--rust-soft);opacity:1;}

  #toast{
    position:fixed;bottom:22px;left:50%;transform:translateX(-50%) translateY(20px);
    background:var(--ink);color:var(--paper);
    padding:10px 18px;border-radius:3px;font-size:13px;
    font-family:'IBM Plex Mono', monospace;
    opacity:0;pointer-events:none;transition:opacity .2s ease, transform .2s ease;
    z-index:50;box-shadow:var(--shadow);
    /* A toast can now carry an Undo button beside its text, and some messages run long enough
       to need wrapping rather than a bar the width of the window. */
    display:flex;align-items:center;gap:14px;
    max-width:min(560px, calc(100vw - 32px));line-height:1.45;text-align:left;
  }
  #toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
  /* Clicks pass straight through a toast that is only telling you something — it sits over the
     table, and a plain message has no business intercepting a click on the row beneath it. Only
     one carrying a button becomes a target, and only while it is up. */
  #toast.show.has-action{pointer-events:auto;}
  .toast-action{
    flex-shrink:0;background:none;cursor:pointer;
    color:var(--paper);font-family:inherit;font-size:12px;
    border:1px solid rgba(246,241,230,0.45);border-radius:3px;padding:4px 10px;
    transition:background .12s ease, border-color .12s ease;
  }
  .toast-action:hover{background:rgba(246,241,230,0.14);border-color:var(--paper);}
  .toast-action:focus-visible{outline:2px solid var(--paper);outline-offset:2px;}

  /* ---------- Controls ---------- */
  .controls{
    display:flex;flex-direction:column;gap:14px;
    background:var(--white);border:1px solid var(--rule);border-radius:4px;
    padding:14px 16px;box-shadow:var(--shadow);
  }
  .controls-row{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;}
  .controls-row + .controls-row{padding-top:14px;border-top:1px solid var(--paper-2);}
  select, input[type=date], input[type=text], input[type=number]{
    font-family:'Inter', sans-serif;
    font-size:13px;
    padding:8px 10px;
    border-radius:3px;
    border:1px solid var(--rule-strong);
    background:var(--white);
    color:var(--ink);
  }
  select:focus, input:focus{outline:2px solid var(--teal);outline-offset:1px;}
  input[type=color]{
    width:42px;height:36px;padding:2px;border-radius:3px;
    border:1px solid var(--rule-strong);background:var(--white);cursor:pointer;
  }
  .field-label{font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:var(--ink-soft);margin-bottom:4px;display:block;}
  .field{display:flex;flex-direction:column;}
  .custom-dates{display:none;gap:10px;}
  .custom-dates.show{display:flex;}
  .spacer{flex:1;}

  /* ---------- Summary cards ---------- */
  .cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:16px;}
  .excluded-note{
    font-size:11.5px;color:var(--ink-soft);margin:10px 2px 0;line-height:1.5;
    font-family:'IBM Plex Mono', monospace;
  }
  .card{
    background:var(--white);border:1px solid var(--rule);border-radius:4px;
    padding:16px 16px 14px;box-shadow:var(--shadow);
    position:relative;
  }
  .card-label{font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:var(--ink-soft);margin:0 0 8px;}
  /* Rides along in the label rather than taking a line of its own — the cards are a fixed row and
     one of them growing taller than the rest would break the baseline they share. */
  .card-qualifier{text-transform:none;letter-spacing:0;color:var(--ink-soft);opacity:.72;font-size:10.5px;}
  .card-value{font-family:'IBM Plex Mono', monospace;font-size:24px;font-weight:600;margin:0;}
  .card-value.rust{color:var(--rust);}
  .card-value.teal{color:var(--teal);}
  .card-sub{font-size:11.5px;color:var(--ink-soft);margin-top:4px;}

  /* ---------- Monthly trend ---------- */
  .section-details{margin:0;}
  /* Section-level disclosure: reads as a section title, not as the small soft summaries
     used by the Manage sub-panels. Shared by every collapsible section. */
  .section-summary{
    font-family:'Fraunces', serif;font-size:20px;font-weight:600;color:var(--ink);
    padding:0;margin-bottom:14px;cursor:pointer;list-style:none;
    display:flex;align-items:center;gap:9px;
  }
  .section-summary::-webkit-details-marker{display:none;}
  /* Same two-stroke caret as the Manage accordion, scaled up for the section title. */
  .section-summary::before{
    content:"";flex-shrink:0;
    width:8px;height:8px;margin:0 1px 0 2px;
    border-right:2px solid var(--rule-strong);
    border-bottom:2px solid var(--rule-strong);
    transform:rotate(-45deg);
    transition:transform .18s cubic-bezier(.4,0,.2,1), border-color .12s ease;
  }
  .section-details[open] > .section-summary::before{transform:rotate(45deg);}
  /* A closed section has nothing under its title, so the gap meant to separate a title from its
     content is just dead space between two collapsed headings. */
  .section-details:not([open]) > .section-summary{margin-bottom:0;}
  .section-summary:hover{color:var(--teal);}
  .section-summary:hover::before{border-color:var(--teal);}
  /* A hint inside a summary sits right, and must not inherit the title's weight. */
  .section-summary .section-hint{
    margin-left:auto;font-family:'Inter', sans-serif;font-weight:400;
    font-size:12.5px;color:var(--ink-soft);
  }
  .section-summary:hover .section-hint{color:var(--ink-soft);}
  .trend-controls{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-bottom:12px;}
  .trend{background:var(--white);border:1px solid var(--rule);border-radius:4px;padding:16px 20px 12px;box-shadow:var(--shadow);}
  .trend-legend{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:14px;}
  .trend-key{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--ink-soft);}
  .trend-swatch{width:10px;height:10px;border-radius:2px;flex-shrink:0;}
  .trend-swatch.in{background:var(--chart-in);}
  .trend-swatch.out{background:var(--chart-out);}
  /* Last year, in year-on-year mode. A muted sibling of --chart-out rather than a third hue: the
     two bars are the same measure at two different times, and giving them unrelated colours would
     read as two unrelated things. */
  .trend-swatch.last{background:var(--chart-last);}
  /* Hovered month's figures. Lives in the header because the plot's horizontal scroller
     clips anything drawn above the bars. */
  .trend-readout{
    margin-left:auto;font-family:'IBM Plex Mono', monospace;font-size:11px;
    color:var(--ink-soft);letter-spacing:0.02em;
    display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;justify-content:flex-end;
    min-height:16px;
  }
  .trend-readout.live{color:var(--ink);}
  .trend-readout b{font-weight:600;}
  .trend-readout .trend-month{color:var(--ink);letter-spacing:0.04em;}
  .trend-readout .trend-sofar{
    color:var(--gold);font-weight:600;text-transform:uppercase;
    font-size:9.5px;letter-spacing:0.08em;
  }
  .trend-plotarea{display:flex;align-items:flex-start;gap:8px;}
  /* Value axis. Sits outside the scroller so it stays put while the bars scroll sideways. */
  .trend-axis{position:relative;width:42px;height:132px;flex-shrink:0;}
  .trend-axis span{
    position:absolute;right:0;transform:translateY(50%);
    font-family:'IBM Plex Mono', monospace;font-size:9.5px;color:var(--ink-soft);
    line-height:1;white-space:nowrap;
  }
  .trend-canvas{position:relative;flex:1;min-width:0;}
  /* Recessive gridlines behind the bars, giving every bar height a value to be read against. */
  .trend-gridlines{position:absolute;left:0;right:0;top:0;height:132px;pointer-events:none;}
  .trend-gridlines i{
    position:absolute;left:0;right:0;height:0;
    border-top:1px dashed var(--rule);
  }
  .trend-gridlines i.base{border-top:1px solid var(--rule-strong);border-top-style:solid;}
  .trend-scroll{overflow-x:auto;position:relative;}
  .trend-plot{
    display:flex;align-items:flex-end;gap:10px;min-width:min-content;padding-bottom:0;
  }
  .trend-col{flex:1 1 0;min-width:34px;display:flex;flex-direction:column;align-items:center;cursor:default;}
  /* Bars hang off a shared baseline; the 2px gap keeps the two fills from reading as one mark. */
  .trend-bars{display:flex;align-items:flex-end;justify-content:center;gap:2px;height:132px;width:100%;}
  .trend-bar{width:11px;border-radius:3px 3px 0 0;transition:height .35s ease;}
  .trend-bar.in{background:var(--chart-in);}
  .trend-bar.out{background:var(--chart-out);}
  .trend-bar.last{background:var(--chart-last);}
  /* One series has the column to itself, so it can afford the width a pair cannot. */
  .trend-bar.wide{width:22px;}
  /* Current month — incomplete. Hatched rather than faded, so it stays as readable as a
     finished month while still reading as "not done yet". */
  .trend-bar.partial{
    background-image:repeating-linear-gradient(135deg,
      rgba(255,253,248,0.62) 0 3px, rgba(255,253,248,0) 3px 7px);
  }
  .trend-label{
    font-family:'IBM Plex Mono', monospace;font-size:10.5px;color:var(--ink-soft);
    padding:7px 0 2px;white-space:nowrap;
  }
  .trend-col:hover .trend-bars,
  .trend-col.active .trend-bars{background:var(--paper-2);border-radius:3px;}
  .trend-col.active .trend-label{color:var(--ink);font-weight:600;}

  /* ---------- Category bars ---------- */
  .bars{background:var(--white);border:1px solid var(--rule);border-radius:4px;padding:20px 20px 8px;box-shadow:var(--shadow);}
  .bar-row{display:grid;grid-template-columns:150px 1fr 90px;align-items:center;gap:12px;padding:9px 0;border-bottom:1px solid var(--paper-2);}
  .bar-row:last-child{border-bottom:none;}
  .bar-cat{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;}
  .dot{width:9px;height:9px;border-radius:50%;flex-shrink:0;}
  .bar-track{background:var(--paper-2);height:16px;border-radius:2px;overflow:hidden;}
  .bar-fill{height:100%;border-radius:2px;transition:width .4s ease;}
  .bar-amt{font-family:'IBM Plex Mono', monospace;font-size:12.5px;text-align:right;color:var(--ink-soft);}
  .empty-note{font-size:13px;color:var(--ink-soft);padding:20px 0;text-align:center;}

  /* ---------- Table ---------- */
  .table-wrap{background:var(--white);border:1px solid var(--rule);border-radius:4px;box-shadow:var(--shadow);overflow:hidden;}
  table{width:100%;border-collapse:collapse;font-size:13px;}
  thead th{
    text-align:left;font-size:10.5px;text-transform:uppercase;letter-spacing:0.06em;
    color:var(--ink-soft);padding:10px 14px;border-bottom:1.5px solid var(--rule-strong);
    background:var(--paper-2);position:sticky;top:0;
  }
  tbody td{padding:10px 14px;border-bottom:1px solid var(--paper-2);vertical-align:middle;}
  tbody tr:hover{background:var(--paper-2);}
  td.desc{max-width:340px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-soft);font-size:12.5px;}
  td.date{font-family:'IBM Plex Mono', monospace;font-size:12px;white-space:nowrap;}
  td.bank{font-size:11.5px;color:var(--ink-soft);white-space:nowrap;text-transform:uppercase;letter-spacing:0.02em;}
  td.amt{font-family:'IBM Plex Mono', monospace;text-align:right;white-space:nowrap;font-weight:600;}
  td.amt.pos{color:var(--teal);}
  td.amt.neg{color:var(--rust);}
  .cat-select{
    font-family:'Inter', sans-serif;font-size:12px;font-weight:600;
    padding:5px 8px;border-radius:12px;border:1px solid transparent;cursor:pointer;
  }
  .manual-flag{font-size:9px;color:var(--gold);margin-left:5px;font-family:'IBM Plex Mono', monospace;}
  .table-scroll{max-height:640px;overflow-y:auto;}
  .preview-scroll{max-height:150px;overflow:auto;border:1px solid var(--rule);border-radius:4px;}
  .preview-scroll table{font-size:11.5px;}
  .preview-scroll th, .preview-scroll td{white-space:nowrap;padding:6px 10px;}
  .table-footnote{padding:10px 14px;font-size:11.5px;color:var(--ink-soft);border-top:1px solid var(--rule);background:var(--paper-2);}

  .modal-desc-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;}

  /* ---------- Description picker ---------- */
  .desc-choices{display:flex;flex-direction:column;gap:6px;}
  .desc-choice{
    display:flex;align-items:baseline;gap:9px;font-size:12.5px;
    padding:8px 11px;border:1px solid var(--rule);border-radius:4px;
    background:var(--white);cursor:pointer;
  }
  .desc-choice:hover{border-color:var(--rule-strong);background:var(--paper-2);}
  .desc-choice:has(input:checked){border-color:var(--teal);background:var(--teal-soft);}
  .desc-tag{
    font-family:'IBM Plex Mono', monospace;font-size:11px;color:var(--gold);
    font-weight:600;flex-shrink:0;
  }
  .desc-val{color:var(--ink);word-break:break-word;}
  .desc-preview{
    background:var(--paper-2);border-radius:4px;padding:10px 12px;
    display:flex;flex-direction:column;gap:5px;
  }
  .desc-preview-row{
    font-size:12.5px;color:var(--ink);font-family:'IBM Plex Mono', monospace;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  }
  .desc-preview-row.muted{color:var(--ink-soft);}
  .desc-preview-row em{font-style:normal;color:var(--rust);font-size:11px;}
  /* the sample is meant to be selected with the mouse */
  #descSample{user-select:text;cursor:text;max-height:120px;overflow-y:auto;}

  /* ---------- Row selection & bulk actions ---------- */
  th.sel, td.sel{width:34px;text-align:center;padding-left:10px;padding-right:0;}
  th.sel input, td.sel input{cursor:pointer;margin:0;vertical-align:middle;}
  tbody tr.selected{background:var(--teal-soft);}
  tbody tr.selected:hover{background:#d2e2db;}
  .bulk-bar{
    display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;
    background:var(--white);border:1px solid var(--rule);border-left:3px solid var(--teal);
    border-radius:4px;padding:12px 16px;margin-bottom:12px;box-shadow:var(--shadow);
  }
  .bulk-count{
    font-family:'IBM Plex Mono', monospace;font-size:12.5px;font-weight:600;
    color:var(--teal);padding-bottom:9px;
  }
  .bulk-field{display:flex;flex-direction:column;}
  .bulk-bar .spacer{flex:1;}

  /* ---------- Category manager ---------- */
  /* .rules-panel is also the card in about.html — the Manage accordion uses .manage-panel. */
  .rules-panel{background:var(--white);border:1px solid var(--rule);border-radius:4px;box-shadow:var(--shadow);padding:16px 18px;}

  .manage-panel{
    background:var(--white);border:1px solid var(--rule);border-radius:6px;
    box-shadow:var(--shadow);overflow:hidden;
  }
  .manage-panel details{margin:0;}
  .manage-panel details + details{border-top:1px solid var(--paper-2);}
  .manage-panel summary{
    display:flex;align-items:center;gap:10px;
    padding:14px 18px;font-size:13px;font-weight:600;color:var(--ink);
    cursor:pointer;list-style:none;user-select:none;
    transition:background .12s ease, color .12s ease;
  }
  .manage-panel summary::-webkit-details-marker{display:none;}
  .manage-panel summary:hover{background:var(--paper-2);}
  .manage-panel details[open] > summary{color:var(--teal);}
  .manage-panel summary:focus-visible{outline:2px solid var(--teal);outline-offset:-2px;}

  /* Chevron drawn from two strokes — a real caret rather than a filled triangle, rotating
     from "points right" (closed) to "points down" (open). */
  .manage-panel summary::before{
    content:"";flex-shrink:0;
    width:6px;height:6px;margin:0 1px 0 2px;
    border-right:1.6px solid var(--rule-strong);
    border-bottom:1.6px solid var(--rule-strong);
    transform:rotate(-45deg);
    transition:transform .18s cubic-bezier(.4,0,.2,1), border-color .12s ease;
  }
  .manage-panel details[open] > summary::before{transform:rotate(45deg);}
  .manage-panel summary:hover::before{border-color:var(--ink-soft);}
  .manage-panel details[open] > summary::before{border-color:var(--teal);}

  .manage-label{flex:1;}
  .count-badge{
    font-family:'IBM Plex Mono', monospace;font-size:10.5px;font-weight:600;
    color:var(--ink-soft);background:var(--paper-2);
    border:1px solid var(--rule);border-radius:10px;
    padding:1px 8px;min-width:24px;text-align:center;
  }
  .manage-panel details[open] > summary .count-badge{
    color:var(--teal);background:var(--teal-soft);border-color:var(--teal-soft);
  }
  .manage-body{padding:2px 18px 18px;}
  .manage-note{
    font-size:12.5px;color:var(--ink-soft);margin:0;line-height:1.55;
    background:var(--paper-2);border-radius:5px;padding:10px 12px;
  }
  .manage-note strong{color:var(--ink);font-weight:600;}
  .manage-note + .manage-note{margin-top:8px;}
  .manage-note + .add-rule-row{margin-top:16px;}
  /* Same trick as .inline-swap below: a keyword named in the instructions wears the chip it will
     appear as in the list underneath, so "first matching rule wins" points at something visible. */
  .inline-kw{
    font-family:'IBM Plex Mono', monospace;font-size:11px;color:var(--ink);
    background:var(--white);border:1px solid var(--rule);border-radius:4px;
    padding:1px 5px;white-space:nowrap;
  }
  /* Mirrors the real toggle, so the instruction shows the thing it's describing. */
  .inline-swap{
    display:inline-flex;align-items:center;justify-content:center;
    width:19px;height:19px;vertical-align:-4px;
    font-family:'IBM Plex Mono', monospace;font-size:10.5px;line-height:1;
    color:var(--ink-soft);background:var(--white);
    border:1.5px solid var(--rule-strong);border-radius:5px;
  }

  .rules-list{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0 16px;}
  .rule-chip{
    display:flex;align-items:center;gap:7px;
    background:var(--white);border:1px solid var(--rule);border-radius:16px;
    padding:5px 6px 5px 12px;font-size:12px;
    transition:border-color .12s ease, box-shadow .12s ease;
  }
  .rule-chip:hover{border-color:var(--rule-strong);box-shadow:0 1px 3px rgba(30,42,46,0.07);}
  .rule-chip button{
    background:none;border:none;color:var(--ink-soft);cursor:pointer;font-size:14px;
    line-height:1;padding:2px 6px;border-radius:50%;
    transition:background .12s ease, color .12s ease;
  }
  .rule-chip button:hover{background:var(--rust-soft);color:var(--rust);}
  .rule-chip button.rename:hover{background:var(--teal-soft);color:var(--teal);}
  /* Must outrank `.rule-chip button` (0,1,1), which would otherwise flatten this back to a
     borderless 50%-radius box — and 50% on a non-square box is an ellipse, which is what made
     the glyph look stretched. Fixed square, so the arrows sit in a proper key. */
  .rule-chip button.exclude-toggle{
    width:21px;height:21px;flex-shrink:0;padding:0;
    display:inline-flex;align-items:center;justify-content:center;
    border-radius:5px;
    background:var(--white);border:1.5px solid var(--rule-strong);
    color:var(--ink-soft);font-family:'IBM Plex Mono', monospace;
    font-size:11px;line-height:1;
  }
  .rule-chip button.exclude-toggle:hover{
    background:var(--paper-2);border-color:var(--ink-soft);color:var(--ink);
  }
  .rule-chip button.exclude-toggle.active{
    color:var(--white);background:var(--gold);border-color:var(--gold);
    font-weight:700;box-shadow:0 0 0 2px #F5EBD6;
  }
  .rule-chip button.exclude-toggle.active:hover{background:#A8781F;border-color:#A8781F;}
  /* Rules, grouped under their category */
  .rule-groups{display:flex;flex-direction:column;gap:0;}
  .rule-group{padding:11px 0;border-bottom:1px solid var(--paper-2);}
  .rule-group:last-child{border-bottom:none;padding-bottom:4px;}
  .rule-group-head{display:flex;align-items:center;gap:8px;margin-bottom:8px;}
  .rule-group-name{font-size:12.5px;font-weight:600;color:var(--ink);}
  .rule-group-count{
    font-family:'IBM Plex Mono', monospace;font-size:10px;color:var(--ink-soft);
    background:var(--paper-2);border-radius:9px;padding:1px 7px;
  }
  .rule-group-chips{display:flex;flex-wrap:wrap;gap:6px;}
  /* Keywords are literal match strings, so they wear the mono face. */
  .keyword-chip{
    display:inline-flex;align-items:center;gap:2px;
    background:var(--paper-2);border:1px solid var(--rule);border-radius:4px;
    padding:2px 2px 2px 8px;
    font-family:'IBM Plex Mono', monospace;font-size:11px;color:var(--ink);
    transition:border-color .12s ease, background .12s ease;
  }
  .keyword-chip:hover{background:var(--white);border-color:var(--rule-strong);}
  .keyword-chip button{
    background:none;border:none;cursor:pointer;color:var(--ink-soft);
    font-size:13px;line-height:1;padding:1px 4px;border-radius:3px;
    transition:background .12s ease, color .12s ease;
  }
  .keyword-chip button:hover{background:var(--rust-soft);color:var(--rust);}
  /* The keyword is the edit control now, so it has to look like text and behave like a button. */
  .keyword-chip button.keyword-text{
    font-family:inherit;font-size:inherit;color:var(--ink);
    padding:1px 2px;text-align:left;
  }
  .keyword-chip button.keyword-text:hover{
    background:none;color:var(--teal);text-decoration:underline;text-underline-offset:2px;
  }
  /* A rule an earlier one already catches. Marked rather than hidden or auto-removed: it is the
     user's rule, and the fix (try it first) may not be the one they want. */
  .keyword-chip.shadowed{border-color:var(--gold);border-style:dashed;}
  .keyword-chip.shadowed .keyword-text{color:var(--ink-soft);text-decoration:line-through;}
  .keyword-chip button.keyword-warn{
    color:var(--gold);font-weight:700;font-size:12px;
    width:16px;text-align:center;padding:1px 0;
  }
  .keyword-chip button.keyword-warn:hover{background:var(--gold);color:var(--white);}

  /* The note above and the controls were flush against each other, and the select sat right
     on top of its preview. Give the row room and let the preview read as an output. */
  .currency-row{
    display:flex;gap:18px;align-items:flex-end;flex-wrap:wrap;
    margin-top:16px;
  }
  .currency-row .field{min-width:210px;}
  .currency-row select{width:100%;}
  .currency-preview{
    font-family:'IBM Plex Mono', monospace;font-size:13px;color:var(--ink);
    background:var(--paper-2);border:1px solid var(--rule);border-radius:4px;
    padding:9px 13px;white-space:nowrap;line-height:1.25;
  }

  /* Storage & backup panel */
  .storage-row{
    display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;
    font-size:12.5px;color:var(--ink-soft);line-height:1.55;
    padding:9px 0;border-bottom:1px solid var(--paper-2);
  }
  .storage-row:last-child{border-bottom:none;}
  .storage-k{
    flex:0 0 74px;font-size:10.5px;text-transform:uppercase;letter-spacing:0.06em;
    color:var(--ink-soft);
  }
  .storage-v{flex:1;min-width:200px;}
  .storage-row.good .storage-k{color:var(--teal);}
  .storage-row.warn .storage-k{color:var(--rust);}
  .storage-row.warn .storage-v{color:var(--ink);}
  .storage-row .btn{margin-left:6px;vertical-align:baseline;}

  /* ---------- Accounts & balances ---------- */
  /* A row, not a chip: the value is an editable field that has to line up down the column so
     the balances can be read as a list and totalled by eye. */
  .account-list{display:flex;flex-direction:column;margin:14px 0 18px;}
  .account-row{
    display:flex;align-items:center;gap:10px;
    background:var(--white);border:1px solid var(--rule);border-radius:4px;
    padding:9px 10px 9px 12px;margin-bottom:6px;
    transition:border-color .12s ease, box-shadow .12s ease;
  }
  .account-row:hover{border-color:var(--rule-strong);box-shadow:0 1px 3px rgba(30,42,46,0.07);}
  .account-kind{
    flex-shrink:0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;
    padding:3px 8px;border-radius:10px;
    color:var(--ink-soft);background:var(--paper-2);border:1px solid var(--rule);
  }
  .account-kind.k-savings{color:var(--teal);background:var(--teal-soft);border-color:var(--teal-soft);}
  .account-kind.k-investment{color:#8A6212;background:#F5EBD6;border-color:#F5EBD6;}
  .account-name{font-size:13px;font-weight:600;color:var(--ink);}
  .account-updated{
    flex:1;min-width:0;text-align:right;
    font-size:11px;color:var(--ink-soft);white-space:nowrap;
  }
  .account-updated.stale{color:var(--rust);}
  .account-row .account-input{
    width:130px;flex-shrink:0;text-align:right;
    font-family:'IBM Plex Mono', monospace;font-size:13px;font-weight:600;
  }
  .account-row button{
    background:none;border:none;color:var(--ink-soft);cursor:pointer;font-size:14px;
    line-height:1;padding:2px 6px;border-radius:50%;
    transition:background .12s ease, color .12s ease;
  }
  .account-row button:hover{background:var(--rust-soft);color:var(--rust);}
  .account-row button.rename:hover{background:var(--teal-soft);color:var(--teal);}
  .account-total{
    display:flex;align-items:baseline;justify-content:space-between;gap:10px;
    margin-top:4px;padding:10px 18px 2px 12px;border-top:1px solid var(--rule);
  }
  .account-total-label{
    font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:var(--ink-soft);
  }
  .account-total-value{font-family:'IBM Plex Mono', monospace;font-size:17px;font-weight:600;}

  /* ---------- Adding an account ---------- */
  #showAddAccountBtn{display:block;width:100%;margin:0 0 16px;}
  .account-form{
    background:var(--paper-2);border:1px solid var(--rule);border-radius:5px;
    padding:14px;margin:0 0 16px;
  }
  /* The name takes the slack; the kind and the value are as wide as their content needs. */
  .account-form-grid{display:flex;flex-wrap:wrap;gap:10px;align-items:flex-end;}
  .account-field-name{flex:1 1 180px;min-width:0;}
  .account-form-actions{display:flex;gap:8px;align-items:center;margin-left:auto;}
  .form-help{font-size:11.5px;color:var(--ink-soft);margin:10px 0 0;}
  .form-error{
    display:flex;align-items:flex-start;gap:6px;
    font-size:12px;color:var(--rust);margin:10px 0 0;
  }
  .form-error::before{content:"!";
    flex-shrink:0;width:15px;height:15px;border-radius:50%;
    background:var(--rust);color:var(--white);
    font-family:'IBM Plex Mono', monospace;font-size:10px;font-weight:700;
    display:inline-flex;align-items:center;justify-content:center;
  }
  input.invalid{border-color:var(--rust);background:#FFF9F6;}

  /* A three-way choice, all of it visible, each segment wearing the colour of the badge it
     produces. Real radios underneath, so arrow keys and labels work without any script. */
  .segmented{display:flex;background:var(--white);border:1px solid var(--rule-strong);
    border-radius:4px;padding:2px;gap:2px;}
  .seg{position:relative;display:flex;margin:0;cursor:pointer;}
  .seg input{position:absolute;width:1px;height:1px;opacity:0;margin:0;pointer-events:none;}
  .seg-face{
    display:inline-flex;align-items:center;
    padding:6px 11px;border-radius:3px;
    font-size:12px;font-weight:600;color:var(--ink-soft);
    white-space:nowrap;transition:background .12s ease, color .12s ease;
  }
  .seg:hover .seg-face{background:var(--paper-2);color:var(--ink);}
  .seg input:checked + .seg-face{color:var(--white);background:var(--ink-soft);}
  .seg input:checked + .seg-face.k-savings{background:var(--teal);}
  .seg input:checked + .seg-face.k-investment{background:var(--gold);}
  .seg input:focus-visible + .seg-face{outline:2px solid var(--teal);outline-offset:1px;}

  /* The symbol sits inside the field so the number is unmistakably money, in the currency the
     rest of the page is using. */
  .money-input{display:flex;align-items:stretch;}
  .money-prefix{
    display:inline-flex;align-items:center;padding:0 8px;
    background:var(--white);border:1px solid var(--rule-strong);border-right:none;
    border-radius:3px 0 0 3px;
    font-family:'IBM Plex Mono', monospace;font-size:12.5px;color:var(--ink-soft);
  }
  .money-input input{
    width:120px;border-radius:0 3px 3px 0;text-align:right;
    font-family:'IBM Plex Mono', monospace;
  }

  /* A real checkbox, visually replaced by the track. Keeping the input in the flow rather than
     display:none is what keeps it focusable and reachable by keyboard. */
  .switch-row{position:relative;display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none;}
  .switch-input{
    position:absolute;width:1px;height:1px;opacity:0;
    margin:0;pointer-events:none;
  }
  .switch-track{
    flex-shrink:0;position:relative;width:38px;height:22px;border-radius:11px;
    background:var(--paper-2);border:1px solid var(--rule-strong);
    transition:background .16s ease, border-color .16s ease;
  }
  .switch-knob{
    position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;
    background:var(--white);box-shadow:0 1px 2px rgba(30,42,46,0.25);
    transition:transform .16s cubic-bezier(.4,0,.2,1);
  }
  .switch-input:checked + .switch-track{background:var(--teal);border-color:var(--teal);}
  .switch-input:checked + .switch-track .switch-knob{transform:translateX(16px);}
  .switch-input:focus-visible + .switch-track{outline:2px solid var(--teal);outline-offset:2px;}
  .switch-label{font-size:12.5px;color:var(--ink-soft);}
  .switch-row:hover .switch-label{color:var(--ink);}

  .add-rule-row{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-end;}
  details{margin-top:6px;}
  summary{cursor:pointer;font-size:13px;font-weight:600;color:var(--ink-soft);padding:4px 0;}
  summary:hover{color:var(--ink);}

  footer{
    text-align:center;font-size:11.5px;color:var(--ink-soft);
    padding:30px 0 10px;font-family:'IBM Plex Mono', monospace;
  }
  .footer-link{color:var(--ink-soft);text-decoration:underline dotted;text-underline-offset:2px;}
  .footer-link:hover{color:var(--teal);}

  /* ---------- Row detail affordance ---------- */
  tbody tr.clickable{cursor:pointer;}
  td.flags{width:60px;text-align:center;white-space:nowrap;}
  tr.add-tx-row td{padding:10px 14px;background:var(--paper-2);text-align:center;border-bottom:1px solid var(--rule);}
  tr.add-tx-row:hover{background:transparent;}
  /* Shared by the table's "+ Add transaction" row and the accounts section's "+ Add an
     account" — one affordance for "append something to this list". */
  .add-row-btn{
    font-family:'Inter', sans-serif;font-size:12.5px;font-weight:600;
    background:none;border:1px dashed var(--rule-strong);color:var(--ink-soft);
    padding:7px 18px;border-radius:20px;cursor:pointer;
  }
  .add-row-btn:hover{border-color:var(--teal);color:var(--teal);background:var(--teal-soft);}
  .flag-icon{font-size:13px;opacity:0.75;margin:0 1px;}

  /* ---------- Modal ---------- */
  .modal-overlay{
    position:fixed;inset:0;background:rgba(20,26,24,0.55);
    display:none;align-items:flex-start;justify-content:center;
    padding:40px 16px;overflow-y:auto;z-index:100;
  }
  .modal-overlay.show{display:flex;}
  .modal-card{
    background:var(--white);border-radius:5px;max-width:560px;width:100%;
    box-shadow:0 20px 60px -12px rgba(0,0,0,0.4);
    animation:modalIn .15s ease;
  }
  @keyframes modalIn{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}
  .modal-head{
    display:flex;justify-content:space-between;align-items:flex-start;
    padding:20px 22px 14px;border-bottom:1px solid var(--rule);
  }
  .modal-date{font-family:'IBM Plex Mono', monospace;font-size:12px;color:var(--ink-soft);margin:0 0 4px;}
  .modal-amt{font-family:'IBM Plex Mono', monospace;font-size:22px;font-weight:600;margin:0;}
  .modal-head-actions{display:flex;align-items:center;gap:8px;flex-shrink:0;}
  .modal-close{
    background:none;border:none;font-size:20px;color:var(--ink-soft);cursor:pointer;
    line-height:1;padding:4px 8px;border-radius:4px;
  }
  .csv-choice{
    display:flex;align-items:center;gap:8px;font-size:13px;
    padding:9px 12px;border:1px solid var(--rule);border-radius:4px;
    background:var(--white);cursor:pointer;margin-bottom:8px;
  }
  .csv-choice:hover{border-color:var(--rule-strong);background:var(--paper-2);}
  .csv-choice:has(input:checked){border-color:var(--teal);background:var(--teal-soft);}
  .modal-close:hover{background:var(--paper-2);color:var(--ink);}
  .modal-body{padding:18px 22px 22px;}
  .modal-section-label{
    font-size:11px;text-transform:uppercase;letter-spacing:0.06em;color:var(--ink-soft);
    margin:0 0 6px;display:block;
  }
  .modal-desc{
    font-size:13px;line-height:1.55;color:var(--ink);background:var(--paper-2);
    border-radius:4px;padding:12px 14px;margin:0 0 10px;word-break:break-word;
    font-family:'IBM Plex Mono', monospace;
  }
  /* Inside the transaction window the description sits on the white body, so it takes the tint
     that the header band has already used up. */
  .tx-card .modal-desc{background:var(--paper);border:1px solid var(--rule);}
  .modal-row{margin-bottom:18px;}
  .note-textarea{
    width:100%;min-height:80px;resize:vertical;font-family:'Inter',sans-serif;
    font-size:13px;padding:10px 12px;border-radius:4px;border:1px solid var(--rule-strong);
    background:var(--white);color:var(--ink);
  }
  .note-textarea:focus{outline:2px solid var(--teal);outline-offset:1px;}
  .save-hint{font-size:10.5px;color:var(--ink-soft);margin-top:4px;font-family:'IBM Plex Mono', monospace;}
  .photo-grid{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:10px;}
  .photo-thumb{
    position:relative;width:84px;height:84px;border-radius:4px;overflow:hidden;
    border:1px solid var(--rule);background:var(--paper-2);
  }
  .photo-thumb img{width:100%;height:100%;object-fit:cover;display:block;cursor:pointer;}
  /* An attachment whose image data didn't pass validation — still removable. */
  .photo-bad{
    display:flex;align-items:center;justify-content:center;width:100%;height:100%;
    color:var(--ink-soft);font-family:'IBM Plex Mono', monospace;font-size:20px;
    background:var(--paper-2);
  }
  .photo-remove{
    position:absolute;top:2px;right:2px;background:rgba(20,26,24,0.7);color:#fff;
    border:none;border-radius:50%;width:19px;height:19px;font-size:12px;cursor:pointer;
    line-height:1;
  }
  .photo-remove:hover{background:var(--rust);}
  .add-photo-tile{
    width:84px;height:84px;border-radius:4px;border:1.5px dashed var(--rule-strong);
    display:flex;align-items:center;justify-content:center;flex-direction:column;
    cursor:pointer;color:var(--ink-soft);font-size:11px;text-align:center;gap:2px;
    background:var(--white);
  }
  .add-photo-tile:hover{border-color:var(--teal);color:var(--teal);background:var(--teal-soft);}
  .add-photo-tile input{display:none;}
  /* ---------- The transaction window ----------
     Three bands: what the bank recorded, what you have added to it, and what you can do about
     either. The amount leads because it is the thing that identifies a row at a glance; the
     category sits on the face of the card because checking it was the commonest reason to open
     this window and it used to be reachable only through the edit form. */
  .tx-card{max-width:520px;}

  .tx-head{
    position:relative;
    background:var(--paper-2);border-bottom:1px solid var(--rule);
    padding:20px 22px 16px;border-radius:5px 5px 0 0;
  }
  .tx-head .modal-close{position:absolute;top:12px;right:12px;}
  .tx-amount{
    font-family:'IBM Plex Mono', monospace;font-size:30px;font-weight:600;
    margin:0;line-height:1.05;letter-spacing:-0.02em;
    /* The column of digits reads as a figure, not as text that happens to be numeric. */
    font-variant-numeric:tabular-nums;
    padding-right:36px;
  }
  .tx-amount.pos{color:var(--teal);}
  .tx-amount.neg{color:var(--rust);}
  .tx-meta{
    font-family:'IBM Plex Mono', monospace;font-size:11.5px;color:var(--ink-soft);
    margin:7px 0 0;
  }
  .tx-tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px;}
  .tx-tag{
    font-size:11px;line-height:1;padding:5px 9px;border-radius:3px;
    border:1px solid var(--rule-strong);color:var(--ink-soft);background:var(--white);
    white-space:nowrap;
  }
  /* The category wears its own colour, the one the table and the bars already give it. */
  .tx-tag-cat{font-weight:600;}

  /* A quiet link under the text it concerns, rather than a button beside it competing for the
     same attention as the description itself. */
  .tx-inline-link{
    background:none;border:none;padding:0;cursor:pointer;
    font-family:'Inter', sans-serif;font-size:12px;color:var(--teal);
    text-decoration:underline;text-underline-offset:2px;text-decoration-style:dotted;
  }
  .tx-inline-link:hover{text-decoration-style:solid;}
  .tx-inline-link:focus-visible{outline:2px solid var(--teal);outline-offset:2px;border-radius:2px;}

  /* Separates what the bank said from what you have added to it. */
  .tx-divider{border-top:1px solid var(--rule);margin:20px 0 18px;}

  .tx-form{
    display:grid;grid-template-columns:1fr 1fr;gap:14px 14px;
  }
  .tx-field{display:flex;flex-direction:column;gap:5px;min-width:0;}
  .tx-field-full{grid-column:1 / -1;}
  .tx-field input,.tx-field select{width:100%;}
  .tx-field .money-input input{width:100%;}
  .tx-form .segmented{align-self:flex-start;}
  .seg input:checked + .seg-face.k-in{background:var(--teal);}
  .seg input:checked + .seg-face.k-out{background:var(--rust);}
  #modalEditFields .form-help{margin-top:14px;}

  .tx-foot{
    display:flex;align-items:center;gap:10px;
    padding:14px 22px;border-top:1px solid var(--rule);
    background:var(--paper-2);border-radius:0 0 5px 5px;
  }
  /* Destructive, and styled like it knows that: the quietest control on the card until you are
     actually pointing at it. It used to sit at the top in full alarm colours, a few pixels from
     the close button. */
  .tx-delete{
    background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;
    font-family:'Inter', sans-serif;font-size:12px;font-weight:600;
    color:var(--ink-soft);padding:6px 10px;
    transition:background .12s ease, color .12s ease, border-color .12s ease;
  }
  .tx-delete:hover{background:var(--rust-soft);color:var(--rust);border-color:var(--rust);}
  .tx-delete:focus-visible{outline:2px solid var(--rust);outline-offset:1px;}

  /* While the form is open, the figure in the header is the one you started from — the live value
     is the field below it. Muting it says so, without throwing away the context of what the
     transaction was before you began. Must sit after .tx-amount.pos/.neg: same specificity, so
     source order is what decides. */
  .tx-card.editing .tx-amount{color:var(--ink-soft);}
  .tx-card.editing .tx-tags{opacity:0.45;}

  /* ---------- Tutorial ----------
     Rules after the modal block on purpose: .tour-title and .modal-amt are both single-class
     selectors, so source order is what lets the serif title win over the mono amount style. */
  .tour-card{max-width:540px;}
  .tour-title{font-family:'Fraunces', serif;font-size:21px;font-weight:600;line-height:1.25;}
  .tour-step{font-size:13.5px;line-height:1.6;}
  .tour-step p{margin:0 0 13px;}
  .tour-step p:last-child{margin-bottom:0;}
  .tour-step ul{margin:0 0 13px;padding-left:20px;}
  .tour-step li{margin-bottom:8px;}
  .tour-step li:last-child{margin-bottom:0;}
  /* The quieter half of a step: true but not instructions. */
  .tour-aside{
    color:var(--ink-soft);background:var(--paper-2);border-radius:4px;
    padding:11px 13px;font-size:13px;
  }
  .tour-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px;}
  .tour-foot{
    display:flex;align-items:center;gap:8px;
    margin-top:22px;padding-top:15px;border-top:1px solid var(--rule);
  }
  .tour-dots{display:flex;gap:7px;}
  .tour-dot{
    width:8px;height:8px;padding:0;border:none;border-radius:50%;
    background:var(--rule-strong);cursor:pointer;
  }
  .tour-dot:hover{background:var(--ink-soft);}
  .tour-dot.on{background:var(--teal);}
  .tour-dot:focus-visible{outline:2px solid var(--teal);outline-offset:2px;}

  /* Drag hint. pointer-events stays off: this appears mid-drag, and an element arriving under
     the cursor can otherwise swallow the drop event it was meant to advertise. */
  .drop-hint{
    position:fixed;inset:0;z-index:150;display:none;pointer-events:none;
    align-items:center;justify-content:center;background:rgba(20,26,24,0.5);
  }
  .drop-hint.show{display:flex;}
  .drop-hint-inner{
    border:2px dashed var(--paper);border-radius:6px;padding:26px 38px;
    font-family:'Fraunces', serif;font-size:22px;color:var(--paper);
    background:rgba(20,26,24,0.4);
  }

  .lightbox{
    position:fixed;inset:0;background:rgba(10,14,13,0.88);display:none;
    align-items:center;justify-content:center;z-index:200;padding:24px;cursor:zoom-out;
  }
  .lightbox.show{display:flex;}
  .lightbox img{max-width:100%;max-height:100%;border-radius:4px;box-shadow:0 20px 60px rgba(0,0,0,0.5);}

  /* ---------- Budgets ---------- */
  .budget-list{display:flex;flex-direction:column;gap:2px;margin-top:14px;}
  .budget-row{
    display:grid;grid-template-columns:150px 1fr 190px;align-items:center;gap:12px;
    padding:9px 0;border-bottom:1px solid var(--paper-2);
  }
  .budget-cat{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink);}
  .budget-track{
    position:relative;background:var(--paper-2);height:16px;border-radius:2px;overflow:hidden;
  }
  .budget-fill{height:100%;border-radius:2px;transition:width .4s ease;}
  /* Where the month itself has got to. Without it a half-full bar on the 28th looks like good
     news and a half-full bar on the 3rd looks the same, which is the opposite of true. */
  .budget-pace{
    position:absolute;top:0;bottom:0;width:2px;background:var(--ink);opacity:0.42;
  }
  .budget-row.over .budget-fill{background:var(--rust) !important;}
  .budget-figures{
    display:flex;align-items:baseline;gap:7px;justify-content:flex-end;
    font-family:'IBM Plex Mono', monospace;font-size:11.5px;
  }
  .budget-spent{color:var(--ink);font-weight:600;}
  .budget-of{color:var(--ink-soft);opacity:.8;}
  .budget-note{color:var(--ink-soft);}
  .budget-row.over .budget-note{color:var(--rust);font-weight:600;}
  .budget-row.ahead .budget-note{color:var(--gold);}

  .budget-edit-list{display:flex;flex-direction:column;gap:2px;margin-top:14px;}
  .budget-edit-row{
    display:flex;align-items:center;gap:10px;padding:7px 0;
    border-bottom:1px solid var(--paper-2);
  }
  .budget-edit-name{flex:1;font-size:12.5px;color:var(--ink);}
  .budget-edit-input{width:150px;flex-shrink:0;}
  .budget-edit-input input{width:100%;}

  /* ---------- The backup reminder ---------- */
  .nudge{
    display:flex;align-items:center;flex-wrap:wrap;
    /* Two gaps rather than one. The wider figure separates the sentence from the actions; the
       narrower one only applies when the banner wraps onto a second line. The mark keeps its own
       tighter gap inside .nudge-body. */
    gap:10px 18px;
    background:var(--gold-soft);border:1px solid var(--gold);border-radius:4px;
    padding:12px 14px;
    /* The same 16px .cards carries, so the banner sits as far below the controls panel as the
       cards used to. It had no top margin at all and was flush against the panel above it, while
       holding 18px underneath — which is what made it look attached to the wrong thing. The
       bottom is left to .cards' own margin-top, which collapses with it to a matching 16px. */
    margin:16px 0 0;
  }
  .nudge-body{
    flex:1 1 260px;min-width:0;
    /* flex-start, not center: when the sentence wraps to two lines the mark belongs beside the
       first one, not floating in the middle of the paragraph. */
    display:flex;align-items:flex-start;gap:8px;
    /* One line-height in px for both children, so a 13px glyph and 12.5px text share a baseline
       instead of each computing its own from a ratio. */
    font-size:12.5px;line-height:19px;color:var(--ink);
  }
  .nudge-mark{flex-shrink:0;color:var(--gold);font-size:13px;}
  .nudge-actions{display:flex;align-items:center;gap:6px;flex-shrink:0;margin-left:auto;}
  .nudge-dismiss{
    display:inline-flex;align-items:center;justify-content:center;
    width:26px;height:26px;padding:0;
    background:none;border:none;border-radius:3px;cursor:pointer;
    color:var(--ink-soft);font-size:18px;line-height:1;
    /* The × carries its own sidebearing, so without this it reads as further from the right edge
       than the mark is from the left. */
    margin-right:-5px;
  }
  .nudge-dismiss:hover{background:var(--white);color:var(--ink);}
  .nudge-dismiss:focus-visible{outline:2px solid var(--gold);outline-offset:1px;}

  /* ---------- What a rule would do ---------- */
  .rule-preview{
    font-size:12px;color:var(--ink-soft);line-height:1.55;margin:10px 0 0;
    padding:8px 11px;border-left:2px solid var(--teal);background:var(--paper-2);border-radius:0 4px 4px 0;
  }
  .rule-preview.warn{border-left-color:var(--gold);color:var(--ink);}

  @media (max-width:600px){
    .brand-title{font-size:32px;}
    .bar-row{grid-template-columns:110px 1fr 70px;gap:8px;}
    /* The figures need the whole width once the track has taken its share. */
    .budget-row{grid-template-columns:1fr 1fr;gap:6px 10px;}
    .budget-track{grid-column:1 / -1;order:3;}
    .budget-figures{justify-content:flex-end;}
    .budget-edit-input{width:120px;}
    .tx-form{grid-template-columns:1fr;}
    .tx-amount{font-size:26px;}
    .tx-head,.tx-foot{padding-left:16px;padding-right:16px;}
    td.desc{max-width:140px;}
    /* Let the chart scroll sideways rather than crushing the bars into slivers. */
    .trend{padding:16px 14px 12px;}
    .trend-plot{gap:6px;}
    .trend-col{min-width:30px;}
    .trend-bar{width:9px;}
    /* The account row runs out of width first: let it wrap and drop the age stamp to its own
       line rather than squeezing the value field. */
    .account-row{flex-wrap:wrap;}
    .account-updated{order:3;flex-basis:100%;text-align:left;}
    .account-row .account-input{width:100px;margin-left:auto;}
  }
