      /* ============================================================
       Second Brain — Journal edition
       Editorial / serif-forward · warm paper · terracotta accent
       Light + dark via html[data-theme]. Variable names kept
       backward-compatible so inline styles keep resolving.
       ============================================================ */

      @font-face {
        font-family: 'DM Sans';
        src: url(../fonts/dm-sans-400.ttf) format('truetype');
        font-weight: 400;
        font-display: swap;
      }
      @font-face {
        font-family: 'DM Sans';
        src: url(../fonts/dm-sans-500.ttf) format('truetype');
        font-weight: 500;
        font-display: swap;
      }
      @font-face {
        font-family: 'DM Sans';
        src: url(../fonts/dm-sans-600.ttf) format('truetype');
        font-weight: 600;
        font-display: swap;
      }
      @font-face {
        font-family: 'Sora';
        src: url(../fonts/sora.ttf) format('truetype');
        font-weight: 100 800;
        font-display: swap;
      }

      *,
      *::before,
      *::after {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
      }

      :root {
        /* surfaces */
        --bg: #ffffff;
        --bg-card: #ffffff;
        --bg-tag: #f3f4f6;
        --bg-secondary: #f3f4f6;
        --bg-confirm: rgba(253, 84, 10, 0.1);
        --bg-tag-confirmed: #fd540a;
        --surface-2: #f3f4f6;
        --surface-3: #e2e4e8;
        --surface-muted: #f3f4f6;
        --charcoal: #17181b;

        /* This surface is deliberately dark in BOTH themes — it is an overlay
           chip (the toast), not a page surface, and its ink is therefore
           fixed rather than theme-flipping. Do not point .app-toast-msg at
           the primary ink token, which flips to near-black in light mode
           and on this near-black surface is how this shipped unreadable
           in the first place. The elevated-accent token exists because the
           page accent cannot be reused for the toast action either: it is
           tuned per theme against each theme's own page surface, but this
           overlay is dark in BOTH themes. Contrast is re-verified by
           toast-contrast.test.ts on every change to these three tokens. */
        --surface-elevated: #17181b;
        --text-on-elevated: #ffffff;
        --accent-on-elevated: #ff9a61;

        /* The knob of an on/off switch (.sb-switch). Fixed near-white in BOTH
           themes, like --text-on-elevated and for the same reason: it never
           sits on the page surface, only on a filled track — --surface-3 when
           off, --accent when on — so a theme-flipping value would go dark on
           dark the moment the switch was turned on. Defined in both blocks
           rather than reached for with a var() fallback: an undefined token
           must fail loudly, which is how the black-on-black toast shipped.
           Contrast re-verified by switch-contrast.test.ts. */
        --switch-knob: #ffffff;

        /* ink */
        --text-primary: #202124;
        --text-secondary: #61646c;
        /* Lighter than --text-secondary on purpose: DESIGN.md's palette has one
           "muted" ink, but the ink ramp needs three visible steps (see
           card-stale-contrast.test.ts). Tuned to the tightest gap that still
           clears AA (4.5:1) on --bg-card and stays a visible step (>1.3:1)
           below --text-secondary. */
        --text-tertiary: #74767d;
        --text-tag: #61646c;
        --text-tag-confirmed: #202124;

        /* lines */
        --border: #e2e4e8;
        --border-card: rgba(32, 33, 36, 0.06);
        --border-input: #e2e4e8;
        --line: #e2e4e8;
        --line-soft: rgba(32, 33, 36, 0.05);
        --border-soft: #d5d8de;

        /* orange roles: brand identity, action buttons, focus/link accent */
        --brand-orange: #fd540a;
        --accent: #cc4109;
        --action: #cc4109;
        --action-hover: #a63508;
        --accent-press: #a63508;
        --accent-soft: rgba(253, 84, 10, 0.1);
        --accent-ink: #bd410e;
        --good: #008300;
        --warn: #b45309;
        --danger: #b42318;
        --danger-soft: rgba(180, 35, 24, 0.08);
        --on-accent: #ffffff;

        /* chart / data series */
        --data: #8a8f99;
        --s1: #fd540a;
        --s2: #2a78d6;
        --s3: #008300;
        --s4: #4a3aa7;
        --s5: #8a8f99;

        /* type: name kept for compatibility, Sora is the display face now */
        --font-serif: 'Sora', 'DM Sans', sans-serif;
        --font-display: 'Sora', 'DM Sans', sans-serif;
        --font-sans: 'DM Sans', system-ui, sans-serif;

        /* radii: old names kept, new living-thread scale added */
        --radius-card: 14px;
        --radius-pill: 999px;
        --radius-tag: 4px;
        --radius-bubble: 14px;
        --r-chip: 4px;
        --r-chip-outline: 5px;
        --r-input: 6px;
        --r-button: 7px;
        --r-card: 12px;
        --r-panel: 14px;

        /* elevation */
        --shadow-card: 0 12px 32px #2021240b;
        --shadow-float: 0 24px 50px rgba(0, 0, 0, 0.18);
        --shadow-sheet: 0 -10px 44px rgba(32, 33, 36, 0.16);

        /* brand mark (S-ribbon), used to replace the brain glyph in identity spots */
        --sb-mark: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAtNTMuMzUgMjcwLjI3IDI3MC4yNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZDU0MGE7fS5jbHMtMntmaWxsOiNmZDZmMTI7fTwvc3R5bGU+PC9kZWZzPjxnPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTS4wNCwxMDUuODNjLS4wMi40MS0uMDQuODQtLjA0LDEuMjZsLjAyLTEuMjZoLjAyWiIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTI2OC43NCwxNjMuNThsLTIxNS44Ny0zLjI0Yy0yOS4xNi0uNDMtNTIuNTYtMjQuMTYtNTIuODctNTMuMjJ2LS4wMmw5NS4wMywxLjQyLDE0Mi45NSw1LjY5YzUuODcsMi43MiwxMS4xMyw2LjUxLDE1LjU4LDExLjE1LDkuNiw5Ljg1LDE1LjQsMjMuMzcsMTUuMTcsMzguMjFaIi8+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNMjcwLjI2LDU3LjVsLTExOS4yNS0xLjc5LTExNC41MS00LjUxQzE1Ljg1LDQzLjQ1LDEuMjYsMjMuMzksMS42MSwwbDIxNS44MSwzLjI0YzE0Ljg0LjIzLDI4LjE3LDYuNDEsMzcuNzIsMTYuMyw5LjUsOS43OSwxNS4yOSwyMy4yNSwxNS4xMywzNy45NloiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0yNzAuMjYsNTcuNXYuMjVjLS4yOSwxNC43LTYuNDcsMjcuOTctMTYuMjQsMzcuNDctNS40LDUuMjMtMTEuOTMsOS4zNi0xOS4xMSwxMS45M2gtLjA2bC04Ny44Ljg5aC0uMzFsLTUxLjU5LjQ5aC0uMTJMMCwxMDcuMTF2LS4wMmMwLS40MS4wMi0uODQuMDQtMS4yNi44Ny0yMi40NCwxNS41Mi00MS4zNiwzNS42NC00OC40OGwxMjUuMjEtMS4yNi05Ljg3LS4zOSwxMTkuMjUsMS43OVoiLz48L2c+PC9zdmc+Cg==');

        /* motion */
        --ease: cubic-bezier(0.16, 1, 0.3, 1);
        --ease-standard: cubic-bezier(0.2, 0, 0, 1);
        --ease-exit: cubic-bezier(0.4, 0, 1, 1);
        /* Name kept for compatibility; the living-thread system has no
           bounce/overshoot curve, so this now matches --ease. */
        --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
        --dur-feedback: 150ms;
        --dur-state: 260ms;
        --dur-layout: 420ms;
        --dur-focal: 700ms;
      }

      html[data-theme='dark'] {
        --bg: #17181b;
        --bg-card: #1e1f23;
        --bg-tag: #24262b;
        --bg-secondary: #24262b;
        --bg-confirm: rgba(255, 154, 97, 0.16);
        --bg-tag-confirmed: #ff9a61;
        --surface-2: #24262b;
        --surface-3: #2f3237;
        --surface-muted: #24262b;
        --charcoal: #0f1012;

        /* Same deliberately-dark overlay surface as the light theme, reusing
           --surface-3 so the toast still separates from --bg. Contrast
           re-verified by toast-contrast.test.ts; see the light-theme comment
           above for why these three stay fixed rather than theme-flipping. */
        --surface-elevated: #2f3237;
        --text-on-elevated: #f3f4f6;
        --accent-on-elevated: #ffab6e;

        /* Not pure white: this theme's lightest ink stays a touch warmer so
           the knob reads as part of the surface, not a hole punched in it.
           Contrast re-verified by switch-contrast.test.ts. */
        --switch-knob: #f3f4f6;

        --text-primary: #f3f4f6;
        --text-secondary: #c3c6cc;
        /* Same ink-ramp requirement as the light theme's comment above. */
        --text-tertiary: #929499;
        --text-tag: #c3c6cc;
        --text-tag-confirmed: #17181b;

        --border: #2f3237;
        --border-card: rgba(255, 255, 255, 0.07);
        --border-input: #3a3d43;
        --line: #2f3237;
        --line-soft: rgba(255, 255, 255, 0.05);
        --border-soft: #454850;

        --brand-orange: #fd540a;
        --accent: #ff9a61;
        --action: #ff9a61;
        --action-hover: #ffab6e;
        --accent-press: #ffab6e;
        --accent-soft: rgba(255, 154, 97, 0.16);
        --accent-ink: #ff9a61;
        --on-accent: #17181b;
        --good: #7bd389;
        --warn: #e0b15e;
        --danger: #ff8a7a;
        --danger-soft: rgba(255, 138, 122, 0.14);

        --data: #9aa0aa;
        --s1: #ff7a3d;
        --s2: #5aa0f2;
        --s3: #3fb554;
        --s4: #9a8cf0;
        --s5: #9aa0aa;

        --shadow-card: 0 12px 32px rgba(0, 0, 0, 0.35);
        --shadow-float: 0 24px 50px rgba(0, 0, 0, 0.55);
        --shadow-sheet: 0 -10px 44px rgba(0, 0, 0, 0.6);
      }

      html,
      body {
        height: 100%;
        background: var(--bg);
        font-family: var(--font-sans);
        font-size: 15px;
        line-height: 1.5;
        color: var(--text-primary);
        -webkit-font-smoothing: antialiased;
        text-rendering: optimizeLegibility;
        overflow: hidden;
      }

      body {
        transition: background 0.45s var(--ease);
      }

      h1,
      h2,
      h3 {
        font-family: var(--font-display);
        font-weight: 500;
        letter-spacing: -0.03em;
      }

      .num {
        font-variant-numeric: tabular-nums;
      }

      i.ti {
        line-height: 1;
      }

      ::selection {
        background: var(--accent-soft);
      }

      #app {
        display: flex;
        flex-direction: column;
        height: 100svh;
        width: 100%;
        max-width: 440px;
        margin: 0 auto;
        background: var(--bg);
        position: relative;
      }

      #sidebar {
        display: none;
      }

      #main-panel {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-width: 0;
        overflow: hidden;
      }

      /* ============ AUTH ============ */
      #auth-overlay {
        position: fixed;
        inset: 0;
        background: var(--bg);
        z-index: 100;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        width: 100%;
        animation: fade-in 0.5s var(--ease);
      }

      @keyframes fade-in {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }

      .auth-card {
        width: 100%;
        max-width: 400px;
        padding: 40px 32px;
        display: flex;
        flex-direction: column;
        align-items: center;
        background: var(--bg);
        border-radius: var(--r-panel);
      }

      .auth-lockup {
        display: block;
        width: 174px;
        height: auto;
        margin-bottom: 28px;
      }

      .auth-link {
        background: none;
        border: none;
        padding: 10px 0 0;
        min-height: 32px;
        font-family: var(--font-sans);
        font-size: 13px;
        color: var(--text-secondary);
        text-decoration: underline;
        cursor: pointer;
      }
      .auth-invite-help {
        margin-top: 6px;
        font-size: 13px;
        line-height: 1.55;
        color: var(--text-secondary);
      }
      .auth-invite-help ol {
        margin: 0;
        padding-left: 18px;
      }
      .auth-invite-help li {
        margin-bottom: 6px;
      }

      /* The lockup image (.auth-lockup) already carries the wordmark
         visually; this heading stays for its accessible name rather than
         showing "Second Brain" twice. */
      #auth-overlay h1 {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
      }

      #auth-overlay p {
        font-family: var(--font-sans);
        font-size: 14px;
        color: var(--text-secondary);
        margin-bottom: 34px;
        text-align: center;
        line-height: 1.6;
        max-width: 300px;
      }

      #auth-overlay .field-group {
        width: 100%;
        display: flex;
        flex-direction: column;
        gap: 11px;
        margin-bottom: 14px;
      }

      /* Inputs: 6px radius, --line borders, per the system's input anatomy. */
      #auth-overlay input {
        width: 100%;
        padding: 14px 16px;
        background: var(--bg-card);
        border: 1px solid var(--line);
        border-radius: var(--r-input);
        font-family: var(--font-sans);
        font-size: 15px;
        color: var(--text-primary);
        outline: none;
        transition:
          border-color 0.18s,
          box-shadow 0.18s;
      }

      #auth-overlay input::placeholder {
        color: var(--text-secondary);
      }

      #auth-overlay input:focus {
        border-color: var(--accent-ink);
        box-shadow: 0 0 0 3px var(--accent-soft);
      }

      /* Connect: the system's primary button, --action fill. */
      #auth-connect {
        width: 100%;
        padding: 15px;
        background: var(--action);
        color: var(--on-accent);
        border: none;
        border-radius: var(--r-button);
        font-family: var(--font-sans);
        font-size: 15px;
        font-weight: 600;
        cursor: pointer;
        transition: background-color var(--dur-feedback) var(--ease-standard);
      }

      #auth-connect:hover {
        background: var(--action-hover);
      }

      #auth-error {
        font-size: 13px;
        color: var(--danger);
        text-align: center;
        margin-top: 10px;
        min-height: 18px;
      }

      /* ============ TOP BAR ============ */
      #topbar {
        display: flex;
        align-items: center;
        gap: 12px;
        height: 60px;
        box-sizing: border-box;
        padding: 8px 12px 8px 20px;
        border-bottom: 1px solid var(--line);
        background: var(--bg);
        flex-shrink: 0;
        position: sticky;
        top: 0;
        z-index: 10;
      }

      #topbar > img {
        width: 32px;
        height: auto;
        flex-shrink: 0;
      }

      /* The rail and top bar render the brand-lockup/brand-mark images
         directly (see index.html); the auth card now does too (.auth-lockup
         above), replacing the icon-font glyph mask this used to be. */

      .topbar-info {
        flex: 1;
        min-width: 0;
      }

      .topbar-name {
        font-family: var(--font-display);
        font-size: 16px;
        font-weight: 600;
        color: var(--text-primary);
        line-height: 1.2;
        letter-spacing: -0.02em;
      }

      .topbar-status {
        font-size: 12px;
        color: var(--text-secondary);
        line-height: 1.2;
        margin-top: 1px;
        display: flex;
        align-items: center;
        gap: 6px;
      }

      .topbar-status::before {
        content: '';
        width: 5px;
        height: 5px;
        border-radius: 50%;
        background: var(--good);
        flex-shrink: 0;
      }

      .topbar-menu {
        background: none;
        border: none;
        cursor: pointer;
        color: var(--text-primary);
        width: 40px;
        height: 40px;
        border-radius: var(--r-button);
        display: flex;
        align-items: center;
        justify-content: center;
        transition:
          background var(--dur-feedback) var(--ease-standard),
          color var(--dur-feedback) var(--ease-standard);
      }

      .topbar-menu i {
        font-size: 20px;
      }

      .topbar-menu:hover {
        background: var(--surface-muted);
      }

      /* ============ SCREENS ============ */
      #screens {
        flex: 1;
        overflow: hidden;
        position: relative;
      }

      .screen {
        display: none;
        flex-direction: column;
        height: 100%;
        position: absolute;
        inset: 0;
      }

      .screen.active {
        display: flex;
        animation: screen-in 0.34s var(--ease);
      }

      @keyframes screen-in {
        from {
          transform: translateY(8px);
        }
        to {
          transform: none;
        }
      }

      #recall-messages {
        flex: 1;
        overflow-y: auto;
        overflow-x: hidden;
        padding: 16px 18px 8px;
        display: flex;
        flex-direction: column;
        gap: 10px;
        -webkit-overflow-scrolling: touch;
        scroll-behavior: smooth;
      }

      /* ============ BOTTOM NAV ============ */
      #navbar {
        display: flex;
        border-top: 1px solid var(--line);
        background: var(--bg);
        flex-shrink: 0;
        padding: 9px 8px calc(16px + env(safe-area-inset-bottom));
      }

      .nav-tab {
        flex: 1;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 4px;
        background: none;
        border: none;
        cursor: pointer;
        padding: 6px 0;
        color: var(--text-secondary);
        position: relative;
        transition: color var(--dur-feedback) var(--ease-standard);
      }

      .nav-tab i {
        font-size: 22px;
      }

      .nav-tab span {
        font-family: var(--font-sans);
        font-size: 11px;
        font-weight: 500;
        letter-spacing: 0.005em;
      }

      .nav-tab.active {
        color: var(--text-primary);
      }

      /* Accent underline at the top of the active tab, mirroring the rail's
         accent dot on the active link. */
      .nav-tab.active::before {
        content: '';
        position: absolute;
        top: 0;
        left: 50%;
        width: 28px;
        margin-left: -14px;
        height: 3px;
        background: var(--accent-ink);
        border-radius: 0 0 3px 3px;
      }

      /* ============ CHAT BUBBLES ============ */
      .bubble-row {
        display: flex;
        animation: bubble-in 0.36s var(--ease) both;
      }

      .bubble-row.user {
        justify-content: flex-end;
      }

      .bubble-row.brain {
        justify-content: flex-start;
      }

      @keyframes bubble-in {
        from {
          transform: translateY(9px);
        }
        to {
          transform: none;
        }
      }

      .bubble {
        max-width: 84%;
        padding: 11px 15px;
        border-radius: var(--radius-bubble);
        font-size: 14px;
        line-height: 1.5;
        word-wrap: break-word;
      }

      .bubble.user {
        background: var(--text-primary);
        color: var(--bg);
        border-bottom-right-radius: 6px;
      }

      .bubble.user .hashtag {
        color: color-mix(in srgb, var(--bg) 68%, var(--accent));
      }

      .bubble.brain {
        background: var(--bg-card);
        color: var(--text-primary);
        border: 0.5px solid var(--border-card);
        border-bottom-left-radius: 6px;
        box-shadow: var(--shadow-card);
      }

      /* Journal serif voice — AI answers + welcome lines */
      .bubble.brain.answer,
      .bubble.brain.voice {
        font-family: var(--font-serif);
        font-size: 16.5px;
        line-height: 1.6;
        letter-spacing: -0.003em;
        padding: 14px 17px;
        max-width: 90%;
      }

      .bubble.brain.answer strong,
      .bubble.brain.voice strong {
        color: var(--accent-ink);
        font-weight: 600;
      }

      .bubble.brain.answer em {
        color: var(--accent-ink);
      }

      .hashtag {
        color: var(--accent-ink);
        font-weight: 500;
      }

      .bubble-time {
        font-size: 11px;
        color: var(--text-secondary);
        padding: 0 6px;
        margin-top: 4px;
        text-align: right;
      }

      .bubble-row.user > div {
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        max-width: 84%;
      }

      /* loading dots */
      .loading-dots {
        display: flex;
        gap: 6px;
        padding: 16px 2px;
        background: transparent;
        border: none;
        border-radius: 0;
        width: fit-content;
        box-shadow: none;
      }

      .loading-dots span {
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: var(--text-secondary);
        animation: bounce 1.3s infinite ease-in-out;
      }

      .loading-dots span:nth-child(2) {
        animation-delay: 0.16s;
      }
      .loading-dots span:nth-child(3) {
        animation-delay: 0.32s;
      }

      @keyframes bounce {
        0%,
        80%,
        100% {
          transform: scale(0.7);
          opacity: 0.4;
        }
        40% {
          transform: scale(1);
          opacity: 1;
        }
      }

      .brain-cards-wrapper {
        display: flex;
        flex-direction: column;
        gap: 9px;
      }

      #recall-welcome {
        gap: 10px;
      }

      /* ============================================================
       CONVERSATION: .ex-q a user bubble, .ex-a an answer on paper
       ============================================================ */
      #recall-messages {
        gap: 14px;
        padding: 20px 20px 10px;
      }

      /* opening hero */
      .recall-hero {
        padding: 6px 0 2px;
        animation: bubble-in 0.4s var(--ease) both;
      }

      .recall-hero .eyebrow {
        font-size: 11px;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: var(--accent-ink);
        margin-bottom: 13px;
      }

      .recall-hero .hero-line {
        font-family: var(--font-sans);
        font-size: 19px;
        line-height: 1.5;
        color: var(--text-primary);
        max-width: 26em;
      }

      .recall-hero .hero-line .hashtag {
        color: var(--accent-ink);
      }

      /* The user's own message: a bubble with one square lower corner (the
         system's device for "this is the speaker"), right-aligned like
         .bubble.user elsewhere in the app. The "You asked" label still
         exists for assistive tech; the bubble shape carries it visually. */
      .ex-q {
        display: flex;
        align-items: baseline;
        gap: 6px;
        align-self: flex-end;
        max-width: 84%;
        padding: 11px 15px;
        border-radius: var(--radius-bubble);
        border-bottom-right-radius: 6px;
        background: var(--text-primary);
        color: var(--bg);
        font-family: var(--font-sans);
        font-size: 14px;
        line-height: 1.5;
        animation: bubble-in 0.36s var(--ease) both;
      }

      .ex-q .q-label,
      .ex-q .q-dash {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
      }

      .ex-q .q-text {
        overflow-wrap: anywhere;
      }

      /* The assistant's answer: paper, a hairline border and the system's
         answer-card shadow, DM Sans rather than the earlier literary serif
         voice reserved for headings and numerals now. */
      .ex-a {
        max-width: 92%;
        padding: 14px 17px;
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: var(--r-card);
        box-shadow: var(--shadow-card);
        font-family: var(--font-sans);
        font-size: 15.5px;
        line-height: 1.6;
        color: var(--text-primary);
        animation: bubble-in 0.36s var(--ease) both;
      }

      .ex-a strong {
        color: var(--accent-ink);
        font-weight: 600;
      }
      .ex-a em {
        color: var(--accent-ink);
        font-style: italic;
      }

      .ex-a > *:first-child {
        margin-top: 0;
      }
      .ex-a > *:last-child {
        margin-bottom: 0;
      }
      .ex-a p {
        margin: 0 0 0.7em;
      }
      .ex-a p:last-child {
        margin-bottom: 0;
      }

      .ex-a h3,
      .ex-a h4 {
        font-family: var(--font-serif);
        font-weight: 600;
        color: var(--text-primary);
        letter-spacing: -0.01em;
        line-height: 1.3;
        margin: 1.1em 0 0.45em;
      }
      .ex-a h3 {
        font-size: 0.92em;
      }
      .ex-a h4 {
        font-size: 0.66em;
        font-weight: 600;
        font-style: normal;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--accent-ink);
        font-family: var(--font-sans);
      }

      .ex-a ul,
      .ex-a ol {
        margin: 0.55em 0 0.8em;
        padding: 0;
        list-style: none;
        counter-reset: exli;
      }
      .ex-a li {
        position: relative;
        padding-left: 1.3em;
        margin-bottom: 0.42em;
        line-height: 1.5;
      }
      .ex-a li:last-child {
        margin-bottom: 0;
      }
      .ex-a ul > li::before {
        content: '';
        position: absolute;
        left: 0.15em;
        top: 0.62em;
        width: 5px;
        height: 5px;
        border-radius: 50%;
        background: var(--accent);
      }
      .ex-a ol > li {
        counter-increment: exli;
      }
      .ex-a ol > li::before {
        content: counter(exli);
        position: absolute;
        left: 0;
        top: 0.04em;
        font-family: var(--font-display);
        font-size: 0.82em;
        color: var(--accent-ink);
        font-variant-numeric: tabular-nums;
      }
      .ex-a code {
        font-family: ui-monospace, 'SF Mono', Menlo, monospace;
        font-size: 0.82em;
        background: var(--surface-2);
        padding: 0.1em 0.4em;
        border-radius: 5px;
      }

      /* system note (no result / error) */
      .recall-sys {
        font-family: var(--font-sans);
        font-size: 15px;
        line-height: 1.55;
        color: var(--text-secondary);
        padding: 8px 0 0;
        animation: bubble-in 0.36s var(--ease) both;
      }

      /* sources, sitting under an answer */
      .ex-a + .sources-toggle,
      .sources-toggle {
        margin-top: 16px;
      }

      /* ---- Remember: editorial capture notes ---- */
      .capture-note {
        position: relative;
        font-family: var(--font-serif);
        font-size: 18px;
        line-height: 1.5;
        color: var(--text-primary);
        padding: 22px 0 4px 16px;
        border-top: 0.5px solid var(--line-soft);
        animation: bubble-in 0.36s var(--ease) both;
      }

      .capture-note::before {
        content: '';
        position: absolute;
        left: 0;
        top: 24px;
        bottom: 11px;
        width: 2px;
        background: var(--accent);
        border-radius: 2px;
      }

      .capture-note .hashtag {
        font-family: var(--font-sans);
        font-size: 13.5px;
        vertical-align: 1px;
      }

      .sys-note {
        font-family: var(--font-serif);
        font-style: italic;
        font-size: 13px;
        color: var(--text-secondary);
        padding: 8px 0 4px 16px;
        animation: bubble-in 0.36s var(--ease) both;
      }

      /* The conversation's own input bar (bottom, #recall-input) now uses the
         same rounded, paper input-bar anatomy as the rest of the system
         instead of a bespoke underline field; see .input-inner/.input-field/
         .send-btn below. #screen-home .composer .send-btn (further down)
         still carries the composer's own 42px square button. */

      /* sources toggle */
      .sources-toggle {
        display: flex;
        flex-direction: column;
        gap: 9px;
        animation: bubble-in 0.36s var(--ease) both;
      }

      /* Secondary button: transparent, hairline border, same anatomy as
         .btn-cancel and the view sheet's Append/Edit. */
      .sources-toggle > button {
        display: inline-flex;
        align-items: center;
        gap: 7px;
        align-self: flex-start;
        height: 32px;
        background: none;
        border: 1px solid var(--line);
        border-radius: var(--r-button);
        cursor: pointer;
        color: var(--text-primary);
        font-family: var(--font-sans);
        font-size: 12.5px;
        font-weight: 500;
        padding: 0 12px;
        transition: background-color var(--dur-feedback) var(--ease-standard);
      }

      .sources-toggle > button i {
        font-size: 15px;
        color: var(--text-secondary);
      }

      .sources-toggle > button:hover {
        background: var(--surface-muted);
      }

      /* ============ MEMORY CARD ============
         System anatomy: paper, 12px radius, a hairline border, no shadow at
         rest. The card shadow appears only on hover and for a cited card. */
      .memory-card {
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: var(--r-card);
        padding: 14px 16px;
        transition:
          box-shadow var(--dur-feedback) var(--ease-standard),
          opacity 0.3s;
        animation: card-in 0.42s var(--ease) both;
      }

      @keyframes card-in {
        from {
          transform: translateY(8px);
        }
        to {
          transform: none;
        }
      }

      .memory-card:hover {
        box-shadow: var(--shadow-card);
      }

      .memory-card.card--rolled-up {
        opacity: 0.65;
      }

      /* Dashed rather than tinted, so it reads as provisional next to
         card--synthesized without touching the card background — which also
         matches how the other card modifiers signal state.

         This once carried a second reason: --text-tertiary sat below WCAG AA on
         --bg-card in both themes, so a tint in either direction pushed one of
         them further down and there was no headroom to spend. That was true of
         tinting THIS card while the token stayed where it was; the token has
         since moved, and both themes now clear AA on every surface. The border
         stays because the first reason still holds. */
      .memory-card.card--stale {
        border-left: 3px dashed var(--warn);
      }

      .memory-card.card--synthesized {
        border-left: 3px solid var(--accent);
      }

      .tag-chip.tag-chip--synthesized {
        background: var(--accent);
        color: var(--on-accent);
        font-weight: 500;
      }

      .vec-chip {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        font-size: 11px;
        padding: 3px 8px;
        border-radius: var(--radius-tag);
        white-space: nowrap;
      }
      .vec-chip--on {
        background: var(--accent-soft);
        color: var(--accent-ink);
      }
      .vec-chip--pending {
        background: var(--surface-2);
        color: var(--text-tag);
      }
      .vec-chip--off {
        background: color-mix(in srgb, var(--danger) 15%, transparent);
        color: var(--danger);
        font-weight: 500;
      }

      .match-line {
        display: flex;
        align-items: center;
        gap: 9px;
        margin-bottom: 9px;
      }

      .match-pct {
        font-size: 10.5px;
        color: var(--accent-ink);
        font-weight: 600;
        font-variant-numeric: tabular-nums;
        flex-shrink: 0;
      }

      .match-bar-bg {
        flex: 1;
        height: 3px;
        background: var(--surface-2);
        border-radius: 2px;
        overflow: hidden;
      }

      .match-bar-fill {
        height: 100%;
        background: var(--accent);
        border-radius: 2px;
        transform-origin: left;
        animation: bar-grow 0.65s var(--ease) both;
      }

      @keyframes bar-grow {
        from {
          transform: scaleX(0);
        }
        to {
          transform: scaleX(1);
        }
      }

      .card-content {
        font-size: 14px;
        color: var(--text-primary);
        line-height: 1.55;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }

      .card-footer {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 10px;
        margin-top: 11px;
      }

      .card-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        min-width: 0;
      }

      .tag-chip {
        background: var(--surface-muted);
        color: var(--text-tag);
        font-size: 12px;
        font-weight: 500;
        padding: 3px 10px;
        border-radius: var(--radius-tag);
        white-space: nowrap;
        font-family: var(--font-sans);
      }

      /* Accent-toned chips. Both were inline styles setting `color: var(--accent)`
         on --surface-2 — 2.84:1 at 11px, the least legible thing on the Memories
         screen and, being inline, invisible to the token that fixed the rest.
         --accent-ink is the same hue at AA. */
      .tag-chip--shared {
        color: var(--accent-ink);
        border-color: var(--accent-ink);
      }

      .tag-chip--hop {
        background: var(--accent-soft);
        color: var(--accent-ink);
        flex-shrink: 0;
      }

      .card-tags .tag-chip:first-child {
        background: var(--accent-soft);
        color: var(--accent-ink);
      }

      .card-actions {
        display: flex;
        gap: 4px;
        flex-shrink: 0;
      }

      .card-action-btn {
        background: none;
        border: none;
        cursor: pointer;
        color: var(--text-secondary);
        font-size: 11px;
        font-family: var(--font-sans);
        display: flex;
        align-items: center;
        gap: 4px;
        padding: 5px 7px;
        border-radius: 8px;
        transition:
          background 0.15s,
          color 0.15s;
      }

      .card-action-btn:hover {
        background: var(--surface-2);
        color: var(--text-primary);
      }

      .card-action-btn i {
        font-size: 14px;
      }

      /* ============ RECENT: a list of memory cards ============ */
      #recent-list {
        flex: 1;
        overflow-y: auto;
        /* The last row used to stop 8px above whatever follows — the tab bar on
           a phone, the panel edge on a desktop — which reads as the list being
           cut off rather than ended. */
        padding: 14px 18px 22px;
        -webkit-overflow-scrolling: touch;
      }

      /* The open-row rule: a date group is a hairline heading, not a box. */
      .date-group {
        margin-bottom: 22px;
      }

      .date-label {
        font-family: var(--font-sans);
        font-weight: 500;
        font-size: 12.5px;
        color: var(--text-secondary);
        text-transform: uppercase;
        letter-spacing: 0.04em;
        margin: 0 2px 10px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--line);
      }

      #recent-list .recent-cards {
        display: flex;
        flex-direction: column;
        gap: 10px;
      }

      #recent-list .card-content {
        font-size: 14.5px;
        line-height: 1.5;
      }

      #recent-list .card-footer {
        margin-top: 8px;
      }

      /* ── The memory is the hero, on a desktop-width card ──────────────────
         Measured before this block: a card was 158px tall and its content was
         46px of that. The memory occupied 29% of its own card. The bands
         collapse into ONE quiet line (source, time, tags and the actions
         share a row and truncate rather than stack) but only where a card is
         wide enough to give tags real room. At 390px the same squeeze left
         Append, Edit and the overflow button (about 170px) and the meta line
         (about 115px) almost nothing, so a tag chip rendered as one clipped
         letter. Below 768px the tags keep their own full-width row instead
         (the general .card-footer rule further down already wraps there). */
      @media (min-width: 768px) {
        #recent-list .card-footer {
          margin-top: 8px;
          gap: 10px;
          flex-wrap: nowrap;
        }

        /* Reading order on the line: where it came from and when, then what it
           is about, then what you can do to it. Provenance first because it is
           the thing that orients you in a list of 2,394. */
        #recent-list .card-meta {
          margin-bottom: 0;
          flex-shrink: 0;
          order: 0;
        }

        /* Shares the meta line and scrolls within it, rather than owning a
           full-width row of its own, room enough here for that to work. */
        #recent-list .card-tags {
          order: 1;
          flex: 1 1 auto;
          min-width: 0;
        }

        #recent-list .card-actions {
          order: 2;
          margin-left: auto;
          flex-shrink: 0;
        }
      }

      /* 44px was well past the 32px this project holds itself to, and it set the
         height of the whole line: three of these were the tallest thing in the
         footer on every card in the list. */
      #recent-list .card-action-btn {
        min-height: 32px;
        padding-top: 0;
        padding-bottom: 0;
      }

      #recent-list .card-action-btn {
        padding: 4px 6px;
        font-size: 10.5px;
        color: var(--text-secondary);
      }

      #recent-list .card-action-btn:hover {
        color: var(--text-primary);
      }

      /* ============ FILTER BAR ============ */
      .filter-bar {
        padding: 8px 18px 10px;
        flex-shrink: 0;
        background: var(--bg);
      }

      /* ── v2.3 Memories: one corpus, two projections ──────────────────────
         Filters sit above the list they narrow. They used to be pinned to the
         bottom, so scanning top-down and deciding to narrow meant travelling to
         the far end of the screen and back. */
      .mem-bar {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 10px 18px;
        flex-shrink: 0;
        background: var(--bg);
        border-bottom: 1px solid var(--line);
      }

      /* Grows to fill a phone, stops well short of it on a wide window — two
         dropdowns stretched across 1,300px read as a form, not a filter. */
      .mem-filters {
        flex: 1;
        min-width: 0;
        max-width: 420px;
      }

      .mem-filters[hidden],
      #mem-legend[hidden] {
        display: none;
      }

      #mem-legend {
        flex: 1;
        min-width: 0;
      }

      .mem-caption {
        font-size: 12.5px;
        color: var(--text-secondary);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      /* A bordered rectangle group, same anatomy as board.css's .seg range
         control: the resting state is a plain outline, the active member
         switches to charcoal fill with white text. */
      .view-toggle {
        display: flex;
        gap: 2px;
        padding: 2px;
        background: none;
        border: 1px solid var(--line);
        border-radius: var(--r-button);
        flex-shrink: 0;
        /* Pinned right in both views. Capping the filters at 420px stopped them
           filling the bar, which left the toggle stranded mid-row in list view
           while graph view — whose caption does stretch — kept it at the edge. */
        margin-left: auto;
      }

      .view-toggle-btn {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        min-height: 32px;
        padding: 6px 12px;
        border: none;
        background: none;
        border-radius: 5px;
        cursor: pointer;
        color: var(--text-secondary);
        font-family: var(--font-sans);
        font-size: 13px;
        font-weight: 500;
        transition:
          background-color var(--dur-state) var(--ease-standard),
          color var(--dur-state) var(--ease-standard);
      }

      .view-toggle-btn i {
        font-size: 16px;
      }

      .view-toggle-btn:hover:not(.active) {
        background: var(--surface-muted);
        color: var(--text-primary);
      }

      .view-toggle-btn.active {
        background: var(--text-primary);
        color: var(--on-accent);
      }

      #recent-list[hidden] {
        display: none;
      }

      /* Filters: 36px rectangles with a hairline border, not pills. They
         switch to charcoal/white when selected, like the view toggle above. */
      .filter-inner {
        display: flex;
        align-items: center;
        min-height: 36px;
        background: var(--bg-card);
        border: 1px solid var(--line);
        border-radius: var(--r-button);
        padding: 8px 10px 8px 14px;
        gap: 8px;
      }

      .filter-field {
        flex: 1;
        background: none;
        border: none;
        outline: none;
        font-family: var(--font-sans);
        font-size: 13px;
        color: var(--text-secondary);
        min-width: 0;
      }

      .filter-field::placeholder {
        color: var(--text-secondary);
      }

      select.filter-field {
        cursor: pointer;
        -webkit-appearance: none;
        appearance: none;
      }

      select.filter-field option {
        color: var(--text-primary);
        background: var(--bg-card);
      }

      .refresh-btn {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        background: var(--surface-2);
        border: none;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        color: var(--text-secondary);
        transition:
          background 0.15s,
          color 0.15s;
      }

      .refresh-btn i {
        font-size: 15px;
      }

      .refresh-btn:hover {
        background: var(--surface-3);
        color: var(--text-primary);
      }

      .refresh-btn.spinning i,
      .refresh-now.spinning i {
        animation: spin 0.7s linear infinite;
      }

      @keyframes spin {
        to {
          transform: rotate(360deg);
        }
      }

      /* ============ SUGGESTIONS ============ */
      .suggestions-row {
        display: flex;
        gap: 8px;
        overflow-x: auto;
        padding: 6px 18px 6px;
        flex-shrink: 0;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
      }

      .suggestions-row::-webkit-scrollbar {
        display: none;
      }

      .suggestion-pill {
        background: var(--bg-card);
        border: 0.5px solid var(--border-input);
        color: var(--text-secondary);
        font-size: 12.5px;
        padding: 7px 14px;
        border-radius: var(--radius-pill);
        white-space: nowrap;
        cursor: pointer;
        font-family: var(--font-sans);
        flex-shrink: 0;
        transition:
          background 0.15s,
          color 0.15s,
          border-color 0.15s;
      }

      .suggestion-pill:hover {
        background: var(--accent-soft);
        border-color: transparent;
        color: var(--accent-ink);
      }

      /* ============ INPUT BAR ============ */
      .input-bar {
        padding: 8px 18px calc(12px + env(safe-area-inset-bottom));
        flex-shrink: 0;
        background: var(--bg);
      }

      .input-inner {
        display: flex;
        align-items: flex-end;
        background: var(--bg-card);
        border: 0.5px solid var(--border-input);
        border-radius: 24px;
        padding: 6px 6px 6px 16px;
        gap: 8px;
        box-shadow: var(--shadow-card);
        transition:
          border-color 0.18s,
          box-shadow 0.18s;
      }

      .input-inner:focus-within {
        border-color: var(--accent);
        box-shadow: 0 0 0 3px var(--accent-soft);
      }

      .input-field {
        flex: 1;
        background: none;
        border: none;
        outline: none;
        font-family: var(--font-sans);
        font-size: 14.5px;
        color: var(--text-primary);
        resize: none;
        line-height: 1.45;
        padding: 7px 0;
        max-height: 80px;
        overflow-y: auto;
      }

      .input-field::placeholder {
        color: var(--text-secondary);
      }

      .send-btn {
        width: 36px;
        height: 36px;
        border-radius: 50%;
        background: var(--accent);
        border: none;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
        transition:
          background 0.18s,
          transform 0.14s var(--ease-spring);
      }

      .send-btn:hover {
        background: var(--accent-press);
      }

      .send-btn:active {
        transform: scale(0.88);
      }

      .send-btn i {
        font-size: 18px;
        color: var(--on-accent);
      }

      .send-btn:disabled {
        opacity: 0.45;
        cursor: default;
      }

      .input-hint {
        font-size: 11.5px;
        color: var(--text-secondary);
        padding: 0 4px 8px;
      }

      .input-hint .hint-tags {
        color: var(--accent-ink);
        font-weight: 500;
      }

      /* ============ CONFIRM BLOCK (remember) ============ */
      .confirm-block {
        background: var(--bg-confirm);
        border-radius: var(--radius-card);
        padding: 11px 14px;
        display: flex;
        flex-direction: column;
        gap: 8px;
        align-self: flex-start;
        max-width: 84%;
        animation: bubble-in 0.36s var(--ease) both;
      }

      .confirm-line {
        display: flex;
        align-items: center;
        gap: 7px;
        font-size: 12.5px;
        color: var(--accent-ink);
        font-weight: 500;
      }

      .confirm-line i {
        font-size: 16px;
        color: var(--accent);
      }

      .confirm-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
      }

      .confirm-tag {
        background: var(--bg-tag-confirmed);
        color: var(--text-tag-confirmed);
        font-size: 11px;
        padding: 3px 10px;
        border-radius: var(--radius-tag);
        font-family: var(--font-sans);
      }

      /* ============ EMPTY STATE ============ */
      .empty-state {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 56px 32px;
        gap: 10px;
        color: var(--text-secondary);
        font-family: var(--font-sans);
        font-size: 15px;
        line-height: 1.5;
        text-align: center;
        height: 100%;
      }

      .empty-state i {
        font-size: 34px;
        margin-bottom: 4px;
        color: var(--text-secondary);
      }

      .first-run-steps {
        margin: 14px 0 0;
        padding-left: 20px;
        text-align: left;
        font-family: var(--font-sans);
        font-size: 14px;
        line-height: 1.65;
        color: var(--text-secondary);
        max-width: 460px;
      }

      .first-run-steps li {
        margin-bottom: 8px;
      }

      /* ============ BOTTOM SHEETS ============ */
      #confirm-dialog,
      #append-sheet,
      #edit-sheet,
      #menu-sheet,
      #integrations-sheet,
      #patterns-sheet,
      #stale-sheet,
      #view-sheet {
        display: none;
        position: fixed;
        inset: 0;
        z-index: 50;
        background: rgba(20, 18, 13, 0.42);
        align-items: flex-end;
        justify-content: center;
        opacity: 0;
        transition: opacity 0.28s var(--ease);
        -webkit-backdrop-filter: blur(2px);
        backdrop-filter: blur(2px);
      }

      /* Action modals paint above the surface that launched them.
         Everything above shares one z-index, so which overlay wins was decided
         by document order — fine while no sheet could open another. The
         out-of-date queue is the first that can: Edit, Append and Forget all
         launch from inside it and all three are declared earlier in index.html,
         so they opened BEHIND it. On a phone that is indistinguishable from the
         tap doing nothing — the sheet does not move, no dialog appears, and the
         confirm is found waiting underneath only after closing the queue.
         Reordering the markup would fix this instance and break on the next
         sheet added below them; the layer is what is actually meant. */
      #confirm-dialog,
      #append-sheet,
      #edit-sheet {
        z-index: 60;
      }

      #confirm-dialog.open,
      #append-sheet.open,
      #edit-sheet.open,
      #menu-sheet.open,
      #integrations-sheet.open,
      #patterns-sheet.open,
      #stale-sheet.open,
      #view-sheet.open {
        display: flex;
        opacity: 1;
      }

      .confirm-sheet,
      .append-inner,
      .edit-inner,
      .menu-inner,
      .view-inner {
        background: var(--bg);
        border-radius: var(--r-panel) var(--r-panel) 0 0;
        width: 100%;
        max-width: 440px;
        box-shadow: var(--shadow-sheet);
        transform: translateY(100%);
        transition: transform 0.34s var(--ease);
      }

      .open .confirm-sheet,
      .open .append-inner,
      .open .edit-inner,
      .open .menu-inner,
      .open .view-inner {
        transform: translateY(0);
      }

      .confirm-sheet {
        padding: 26px 24px calc(34px + env(safe-area-inset-bottom));
      }
      .append-inner {
        padding: 24px 22px calc(34px + env(safe-area-inset-bottom));
      }
      .edit-inner {
        padding: 24px 22px calc(34px + env(safe-area-inset-bottom));
      }
      .menu-inner {
        padding: 24px 22px calc(34px + env(safe-area-inset-bottom));
        max-height: 80vh;
        overflow-y: auto;
      }

      .view-inner {
        padding: 22px 22px calc(34px + env(safe-area-inset-bottom));
        display: flex;
        flex-direction: column;
        max-height: 90vh;
      }

      /* Sheet and dialog titles: Sora 17px as h2, per the system's panel
         anatomy (confirm, append, edit, view, menu, integrations, patterns
         and stale sheets all promoted from h3, same ids). */
      .confirm-sheet h2,
      .append-inner h2,
      .edit-inner h2,
      .menu-inner h2,
      .view-header h2 {
        font-family: var(--font-display);
        font-size: 17px;
        font-weight: 500;
        margin-bottom: 9px;
        letter-spacing: -0.02em;
        color: var(--text-primary);
      }

      .confirm-sheet p {
        font-family: var(--font-sans);
        font-size: 15px;
        color: var(--text-secondary);
        margin-bottom: 22px;
        line-height: 1.55;
      }

      /* The sheet's optional modifier — "and also delete the memories". A full
         32px row because the whole label is the tap target, and secondary ink
         because it is a qualifier on the question above, not the question. */
      .confirm-check {
        display: flex;
        align-items: center;
        gap: 9px;
        font-size: 13.5px;
        color: var(--text-secondary);
        margin-bottom: 18px;
        min-height: 32px;
        cursor: pointer;
      }

      .view-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 14px;
        flex-shrink: 0;
      }

      .btn-close-icon {
        background: var(--surface-2);
        border: none;
        color: var(--text-secondary);
        width: 32px;
        height: 32px;
        border-radius: 50%;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background 0.15s;
      }

      .btn-close-icon i {
        font-size: 18px;
      }
      .btn-close-icon:hover {
        background: var(--surface-3);
      }

      .view-content {
        font-family: var(--font-sans);
        font-size: 15px;
        color: var(--text-primary);
        line-height: 1.6;
        overflow-y: auto;
        margin-bottom: 18px;
        white-space: pre-wrap;
        /* pre-wrap wraps at spaces but will not break a long unbroken token, so
           one file path or URL pushed the whole sheet sideways and the text
           scrolled off the left edge on a phone. */
        overflow-wrap: anywhere;
        word-break: break-word;
      }

      .view-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        margin-bottom: 22px;
        flex-shrink: 0;
      }

      .view-actions {
        display: flex;
        gap: 8px;
        flex-shrink: 0;
        /* Separated from the scrolling region above rather than pushed down by
           margin-top:auto, which did nothing once the content overflowed. The
           rule is not decoration: a long memory scrolls underneath this row, and
           without it the text simply stopped mid-sentence at the buttons with
           nothing saying the rest was still there. */
        padding-top: 14px;
        border-top: 1px solid var(--line);
      }

      /* View-sheet actions: Append and Edit are secondary (transparent,
         hairline border); Forget is destructive (--danger, white label),
         the same treatment as the confirm sheet's own Forget button. */
      .btn-view-append,
      .btn-view-edit,
      .btn-view-forget {
        flex: 1;
        padding: 13px;
        border: 1px solid var(--line);
        border-radius: var(--r-button);
        font-family: var(--font-sans);
        font-size: 13.5px;
        font-weight: 500;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        background: transparent;
        color: var(--text-primary);
        transition:
          background-color var(--dur-feedback) var(--ease-standard),
          border-color var(--dur-feedback) var(--ease-standard),
          color var(--dur-feedback) var(--ease-standard);
      }

      .btn-view-append i,
      .btn-view-edit i,
      .btn-view-forget i {
        font-size: 16px;
      }

      .btn-view-append:hover,
      .btn-view-edit:hover {
        background: var(--surface-muted);
      }

      .btn-view-forget {
        background: var(--danger);
        border-color: var(--danger);
        /* --on-accent, not a hardcoded white: --danger flips light-on-dark
           in dark theme (a pale coral), so a fixed white label would read at
           roughly 1.5:1 there. --on-accent flips the same way --danger does
           and lands on dark ink exactly when the fill goes pale. */
        color: var(--on-accent);
      }
      .btn-view-forget:hover {
        opacity: 0.92;
      }

      .confirm-actions,
      .append-actions {
        display: flex;
        gap: 10px;
      }

      /* Secondary: transparent with a hairline border. */
      .btn-cancel,
      .btn-append-cancel {
        flex: 1;
        padding: 14px;
        background: transparent;
        border: 1px solid var(--line);
        border-radius: var(--r-button);
        font-family: var(--font-sans);
        font-size: 14.5px;
        font-weight: 500;
        color: var(--text-primary);
        cursor: pointer;
        transition: background-color var(--dur-feedback) var(--ease-standard);
      }

      .btn-cancel:hover,
      .btn-append-cancel:hover {
        background: var(--surface-muted);
      }

      /* Destructive: --danger fill, --on-accent label (see .btn-view-forget:
         a hardcoded white fails once --danger goes pale in dark theme). */
      .btn-delete {
        flex: 1;
        padding: 14px;
        background: var(--danger);
        border: 1px solid var(--danger);
        border-radius: var(--r-button);
        font-family: var(--font-sans);
        font-size: 14.5px;
        font-weight: 600;
        color: var(--on-accent);
        cursor: pointer;
        transition: opacity 0.15s;
      }

      .btn-delete:hover {
        opacity: 0.92;
      }

      /* Primary: --action fill, white label. */
      .btn-append-save {
        flex: 1;
        padding: 14px;
        background: var(--action);
        border: 1px solid var(--action);
        border-radius: var(--r-button);
        font-family: var(--font-sans);
        font-size: 14.5px;
        font-weight: 600;
        color: var(--on-accent);
        cursor: pointer;
        transition: background-color var(--dur-feedback) var(--ease-standard);
      }

      .btn-append-save:hover {
        background: var(--action-hover);
        border-color: var(--action-hover);
      }

      .btn-append-save:disabled {
        opacity: 0.55;
        cursor: default;
      }

      .append-context {
        font-family: var(--font-sans);
        font-size: 13.5px;
        color: var(--text-secondary);
        margin-bottom: 16px;
        line-height: 1.5;
        padding: 12px 14px;
        background: var(--surface-muted);
        border: 1px solid var(--border-card);
        border-radius: var(--r-card);
        display: -webkit-box;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }

      .append-input-wrap {
        background: var(--bg-card);
        border: 0.5px solid var(--border-input);
        border-radius: 13px;
        padding: 13px 15px;
        margin-bottom: 14px;
        transition:
          border-color 0.18s,
          box-shadow 0.18s;
      }

      .append-input-wrap:focus-within {
        border-color: var(--accent);
        box-shadow: 0 0 0 3px var(--accent-soft);
      }

      .append-input-wrap textarea {
        width: 100%;
        background: none;
        border: none;
        outline: none;
        font-family: var(--font-sans);
        font-size: 14.5px;
        color: var(--text-primary);
        resize: none;
        line-height: 1.5;
        min-height: 60px;
      }

      .append-input-wrap textarea::placeholder {
        color: var(--text-secondary);
      }

      .edit-existing-tags {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        margin-bottom: 14px;
      }

      .edit-hint {
        font-size: 12px;
        color: var(--text-secondary);
        margin-bottom: 14px;
      }

      .edit-hint span {
        color: var(--accent-ink);
      }

      /* ── v2.3: the editor claims the height it needs ──────────────────────
         This is the only sheet whose whole job is a long piece of text, so it
         takes the screen rather than being sized to fit around its own
         furniture. It used to read the textarea's scrollHeight while the sheet
         was still display:none — which is 0 — and cap the result at 200px, so a
         two-thousand-character memory was edited through about three visible
         lines. Height is CSS's job now and nothing measures anything. */
      /* A definite height, not a max-height, and that is the whole trick.
         flex-grow only hands out *free* space, and a container sized by its own
         content has none — so `flex: 1 1 auto` on the editor below did nothing
         and the textarea fell back to a 60px floor on a 20,000-character
         memory. Fixed rather than content-sized is also the right shape for a
         work surface: the box should not jump size between memories.

         overflow:hidden so nothing can escape the padding box. Without it a
         sheet whose children declare more height than it has renders them over
         its own padding, flush to the card's edge. */
      .edit-inner {
        display: flex;
        flex-direction: column;
        height: 82vh;
        max-height: 82vh;
        overflow: hidden;
      }

      .edit-header {
        flex-shrink: 0;
      }

      .edit-inner .edit-header h3 {
        margin-bottom: 3px;
      }

      /* Which memory this is, since the sheet is otherwise identical whichever
         row opened it. */
      .edit-sub {
        font-size: 12.5px;
        color: var(--text-secondary);
        margin-bottom: 14px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      /* Takes everything the tags, hint and buttons do not. The min-height is
         only a floor for a very short window; the height comes from the grow. */
      .edit-input-wrap {
        flex: 1 1 auto;
        min-height: 120px;
        display: flex;
        overflow: hidden;
      }

      .edit-input-wrap textarea {
        flex: 1 1 auto;
        /* `height: 100%` resolved against the wrapper in a way that left the
           textarea at its own 60px minimum; plain stretch fills it. */
        height: auto;
        align-self: stretch;
        overflow-y: auto;
        overflow-wrap: anywhere;
      }

      /* Sized to its contents, with a safety valve rather than a tight cap. A
         fixed 62px clipped the third row of chips halfway through, which reads
         as a broken layout rather than as something that scrolls — and the
         editor above is protected by its own min-height either way. */
      .edit-existing-tags {
        flex-shrink: 0;
        max-height: 20vh;
        overflow-y: auto;
        margin-top: 14px;
      }

      .edit-hint,
      .edit-inner .append-actions {
        flex-shrink: 0;
      }

      /* Tags could be added but never removed anywhere in the product, while
         these chips sat next to a hint about adding them — so they looked like
         a control and were not one. */
      .tag-chip--removable {
        display: inline-flex;
        align-items: center;
        gap: 4px;
        border: 1px solid var(--line);
        cursor: pointer;
        font-family: var(--font-sans);
        transition:
          background 0.15s,
          color 0.15s;
      }

      .tag-chip--removable i {
        font-size: 12px;
        opacity: 0.5;
      }

      .tag-chip--removable:hover {
        background: var(--danger-soft);
        color: var(--danger);
      }

      .tag-chip--removable:hover i {
        opacity: 1;
      }

      /* ============ MENU SHEET ============ */
      .menu-inner h2 {
        margin-bottom: 18px;
      }

      .theme-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 4px 0 16px;
        margin-bottom: 4px;
      }

      .theme-row-label {
        font-size: 14px;
        color: var(--text-secondary);
      }

      .theme-toggle {
        display: flex;
        gap: 3px;
        background: var(--surface-2);
        border-radius: var(--radius-pill);
        padding: 3px;
      }

      .theme-toggle button {
        background: none;
        border: none;
        cursor: pointer;
        padding: 7px 13px;
        border-radius: var(--radius-pill);
        font-family: var(--font-sans);
        font-size: 12.5px;
        color: var(--text-secondary);
        display: flex;
        align-items: center;
        gap: 5px;
        transition:
          background 0.18s,
          color 0.18s;
      }

      .theme-toggle button i {
        font-size: 16px;
      }

      .theme-toggle button.active {
        background: var(--bg-card);
        color: var(--accent-ink);
        box-shadow: var(--shadow-card);
      }

      .menu-item {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 15px 0;
        border-top: 1px solid var(--line);
        cursor: pointer;
        color: var(--text-primary);
        font-size: 15px;
        background: none;
        border-left: none;
        border-right: none;
        border-bottom: none;
        width: 100%;
        font-family: var(--font-sans);
        text-align: left;
        transition: color 0.15s;
      }

      .menu-item:hover {
        color: var(--text-secondary);
      }

      .menu-item i {
        font-size: 20px;
        color: var(--text-secondary);
      }

      .menu-item.danger {
        color: var(--danger);
      }
      .menu-item.danger i {
        color: var(--danger);
      }

      .about-credits {
        margin: 18px 0 14px;
        padding: 14px 16px;
        background: var(--surface-muted);
        border: 1px solid var(--border-card);
        border-radius: var(--r-card);
        font-size: 13px;
        color: var(--text-secondary);
        line-height: 1.5;
      }

      .about-credits-label {
        font-family: var(--font-sans);
        font-size: 15px;
        font-weight: 500;
        color: var(--text-primary);
        margin-bottom: 10px;
      }

      .about-credits-kicker {
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        font-weight: 500;
        color: var(--text-secondary);
        margin: 0 0 6px;
      }

      .about-credits-line {
        margin: 0 0 12px;
      }

      .about-credits-list {
        margin: 0;
        padding-left: 18px;
      }

      .about-credits-list li {
        margin-bottom: 4px;
      }

      .about-credits a {
        color: var(--accent-ink);
        text-decoration: none;
      }

      .about-credits a:hover {
        text-decoration: underline;
      }

      /* ── v2.3 settings sheet: grouped by what the thing is ───────────────
         It was fourteen items in one stack — a stats panel, five chore panels, a
         theme toggle, three data actions and an about block — with nothing
         saying which was which. The Tauri Advanced Settings window groups by
         what a control affects; this borrows that and nothing else, because its
         staged Save/Cancel model would be wrong for a sheet whose controls are
         all immediate verbs. The stats panel is gone entirely: home shows the
         count, the week, the sources and the tags. */
      .menu-group {
        margin-bottom: 18px;
      }

      .menu-group[hidden] {
        display: none;
      }

      /* Deliberately unlike .digest-section-label, which is the 11px uppercase
         tertiary style used *inside* a group. Two identically-styled labels
         stacked ("UPKEEP" over "READY TO COMPRESS") read as two peers rather
         than as a heading and its contents. */
      .menu-group-label {
        font-family: var(--font-sans);
        font-size: 15px;
        font-weight: 500;
        color: var(--text-primary);
        margin-bottom: 10px;
        padding-bottom: 7px;
        border-bottom: 1px solid var(--line);
      }

      .digest-section {
        margin-bottom: 6px;
      }

      .digest-section-label {
        font-size: 11px;
        color: var(--text-secondary);
        text-transform: uppercase;
        letter-spacing: 0.06em;
        font-weight: 500;
        margin-bottom: 9px;
      }

      /* ── v2.3 pattern review queue ────────────────────────────────────────
         Home shows two patterns as a daily nudge; this is the backlog, which on
         a brain that has been running a while is hundreds long. It is its own
         sheet rather than a panel inside settings for that reason. */
      .patterns-inner {
        display: flex;
        flex-direction: column;
        max-height: 88vh;
      }

      .patterns-bulkbar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        flex-shrink: 0;
        padding: 10px 0;
        margin-bottom: 4px;
        border-top: 1px solid var(--line);
        border-bottom: 1px solid var(--line);
        position: sticky;
        top: 0;
        background: var(--bg);
        z-index: 1;
      }

      .patterns-bulkbar[hidden] {
        display: none;
      }

      .patterns-selectall {
        display: inline-flex;
        align-items: center;
        gap: 9px;
        font-size: 13.5px;
        color: var(--text-secondary);
        cursor: pointer;
        min-width: 0;
      }

      .patterns-bulk-actions {
        display: flex;
        gap: 8px;
        flex-shrink: 0;
      }

      #patterns-list {
        flex: 1 1 auto;
        overflow-y: auto;
        min-height: 0;
        padding-bottom: 6px;
      }

      /* The whole row is the label, so tapping anywhere on a pattern ticks it —
         a checkbox-sized target is the wrong size for a phone. */

      /* One flagged memory, with the four ways out of the flag. Mirrors
         .pattern-row's rhythm; no checkbox, because ruling on staleness is a
         per-memory judgement rather than a bulk one. */
      .stale-row {
        padding: 12px 2px;
        border-bottom: 1px solid var(--line);
      }

      .stale-row:last-child {
        border-bottom: none;
      }

      .stale-text {
        margin: 0 0 6px;
        font-size: 14px;
        line-height: 1.5;
        color: var(--text-primary);
      }

      .stale-when {
        display: block;
        font-size: 12px;
        color: var(--text-secondary);
        margin-bottom: 8px;
      }

      .stale-actions {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
      }

      .pattern-row {
        display: flex;
        align-items: flex-start;
        gap: 11px;
        padding: 12px 2px;
        border-bottom: 1px solid var(--line);
        cursor: pointer;
      }

      .pattern-row:last-child {
        border-bottom: none;
      }

      .pattern-check,
      #patterns-select-all {
        flex-shrink: 0;
        width: 17px;
        height: 17px;
        margin: 2px 0 0;
        accent-color: var(--accent);
        cursor: pointer;
      }

      .pattern-body {
        display: flex;
        flex-direction: column;
        gap: 4px;
        min-width: 0;
      }

      .pattern-text {
        font-family: var(--font-sans);
        font-size: 15px;
        line-height: 1.5;
        color: var(--text-primary);
        overflow-wrap: anywhere;
      }

      .pattern-when {
        font-size: 11.5px;
        color: var(--text-secondary);
      }

      .pattern-sources {
        display: flex;
        flex-direction: column;
        gap: 3px;
        margin: 2px 0 0;
        padding: 0 0 0 14px;
        list-style: disc;
      }

      .pattern-source {
        font-size: 12.5px;
        line-height: 1.4;
        color: var(--text-secondary);
        overflow-wrap: anywhere;
      }

      .pattern-source--gone {
        color: var(--text-secondary);
        font-style: italic;
      }

      #patterns-more {
        flex-shrink: 0;
        align-self: flex-start;
      }

      #patterns-more[hidden] {
        display: none;
      }

      .digest-more {
        background: none;
        border: none;
        padding: 6px 0;
        cursor: pointer;
        color: var(--text-secondary);
        font-family: var(--font-sans);
        font-size: 13px;
      }

      .digest-more:hover {
        color: var(--accent-ink);
      }

      .digest-candidate-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        padding: 8px 0;
        border-bottom: 0.5px solid var(--line-soft);
      }

      .digest-candidate-row:last-of-type {
        border-bottom: none;
      }

      .digest-candidate-label {
        font-size: 13px;
        color: var(--text-secondary);
        display: flex;
        align-items: center;
        gap: 6px;
      }

      .digest-candidate-count {
        font-size: 11px;
        color: var(--text-secondary);
      }

      /* Browsers do not inherit font-family into form controls: a <button>,
         <select>, <input> or <textarea> falls back to a system font unless you
         say otherwise. Half the controls in this app — 207 of 416 — were
         rendering in Arial next to DM Sans copy, which is most of why the
         interface read as unfinished.

         font-family only, not the `font` shorthand: size, weight and
         line-height are set deliberately per control and inheriting them would
         flatten that. */
      button,
      select,
      input,
      textarea {
        font-family: inherit;
      }

      .digest-btn {
        font-size: 12px;
        font-weight: 500;
        color: var(--text-primary);
        background: transparent;
        border: 1px solid var(--line);
        border-radius: 7px;
        padding: 5px 12px;
        cursor: pointer;
        white-space: nowrap;
        transition: background 0.15s;
      }

      .digest-btn:hover {
        background: var(--surface-muted);
      }

      .digest-btn:disabled {
        opacity: 0.5;
        cursor: not-allowed;
      }

      .digest-btn--loading i {
        animation: spin 0.7s linear infinite;
        display: inline-block;
      }

      .digest-btn--primary,
      .confirm-accept-primary {
        background: var(--action);
        border-color: var(--action);
        color: var(--on-accent);
      }

      .digest-btn--primary:hover,
      .confirm-accept-primary:hover { background: var(--action-hover); }


      .digest-note {
        font-size: 12px;
        color: var(--text-secondary);
        line-height: 1.5;
        margin-bottom: 10px;
      }

      .digest-note a {
        color: var(--accent-ink);
        text-decoration: none;
        border-bottom: 1px solid var(--accent-soft);
      }

      .digest-note a:hover {
        border-bottom-color: var(--accent);
      }

      .integration-row {
        padding: 16px 0;
      }

      .integration-row + .integration-row {
        border-top: 1px solid var(--line);
      }

      .integration-category-row {
        display: flex;
        align-items: center;
        gap: 11px;
        width: 100%;
        padding: 15px 4px;
        background: transparent;
        border: none;
        border-bottom: 1px solid var(--line);
        font-family: inherit;
        font-size: 14px;
        color: var(--text-primary);
        cursor: pointer;
        text-align: left;
      }
      .integration-category-row:last-child {
        border-bottom: none;
      }
      .integration-category-row:hover {
        background: rgba(128, 128, 128, 0.08);
      }
      .integration-category-row > i.ti {
        color: var(--accent);
        font-size: 18px;
      }
      .integration-category-name {
        font-weight: 500;
      }
      .integration-category-summary {
        margin-left: auto;
        font-size: 12px;
        color: var(--text-secondary);
      }
      .integration-category-chevron {
        color: var(--text-secondary) !important;
        font-size: 16px !important;
      }

      .integration-head {
        display: flex;
        align-items: center;
        gap: 7px;
        font-size: 13px;
        font-weight: 500;
        color: var(--text-primary);
        margin-bottom: 6px;
      }

      .integration-head i {
        color: var(--accent);
        font-size: 15px;
      }

      .integration-state {
        margin-left: auto;
        font-size: 11px;
        color: var(--text-secondary);
      }

      .integration-state.connected {
        color: var(--good);
      }

      .integration-connect-row {
        display: flex;
        gap: 8px;
        margin-bottom: 6px;
      }

      .integration-connect-col {
        flex-direction: column;
        align-items: stretch;
      }

      .integration-connect-row input {
        flex: 1;
        min-width: 0;
        padding: 8px 10px;
        background: transparent;
        border: 1px solid var(--line);
        border-radius: var(--r-input);
        font-size: 13px;
        font-family: inherit;
        color: var(--text-primary);
        outline: none;
      }

      .integration-connect-row input:focus {
        border-color: var(--accent);
      }

      .integration-actions {
        display: flex;
        gap: 8px;
      }

      .digest-btn.danger {
        color: var(--danger);
        background: rgba(179, 38, 30, 0.08);
      }

      .integration-error {
        font-size: 12px;
        color: var(--danger);
        margin-bottom: 6px;
      }

      .digest-result {
        font-size: 12px;
        color: var(--text-secondary);
        margin-top: 6px;
        line-height: 1.5;
        padding: 8px 10px;
        background: var(--accent-soft);
        border-radius: 8px;
      }

      .digest-result-meta {
        margin-top: 6px;
        font-size: 11px;
        color: var(--good);
        font-weight: 500;
      }

      /* ============ DESKTOP SIDEBAR ============ */
      #sidebar {
        width: 232px;
        flex-shrink: 0;
        flex-direction: column;
        gap: 6px;
        background: var(--bg);
        border-right: 1px solid var(--line);
        padding: 26px 22px 22px;
      }

      .brand {
        display: block;
        width: 174px;
        margin: 2px 0 0 2px;
      }

      .brand img {
        display: block;
        width: 100%;
        height: auto;
      }

      /* The ink lockup's wordmark is unreadable on a dark ground; swap in the
         reverse (white-text) mark, same asset the site's own header/mobile
         menu convention uses. */
      html[data-theme='dark'] .brand img,
      html[data-theme='dark'] .auth-lockup {
        content: url(../brand-lockup-reverse.png);
      }

      .rail-status {
        margin: 14px 0 22px 2px;
      }

      /* The team a member belongs to, between the product name and the status
         line. Sized between the two so it reads as the middle of a hierarchy
         rather than a second title, and truncated rather than wrapped — the
         sidebar is a fixed column and a two-line team name would push the nav. */
      .topbar-team {
        font-size: 11.5px;
        font-weight: 500;
        line-height: 1.35;
        color: var(--accent-ink);
        margin-top: 1px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .sb-brand-team {
        font-size: 13px;
        font-weight: 500;
        line-height: 1.4;
        color: var(--accent-ink);
        max-width: 188px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .sb-brand-status {
        font-size: 13px;
        color: var(--text-secondary);
        margin-top: 2px;
      }

      .sb-nav {
        display: flex;
        flex-direction: column;
        gap: 2px;
      }

      .sb-tab {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 40px;
        padding: 0 10px 0 22px;
        border: 0;
        border-radius: var(--r-button);
        background: none;
        cursor: pointer;
        color: var(--text-secondary);
        font-size: 14px;
        font-weight: 500;
        font-family: var(--font-sans);
        width: 100%;
        text-align: left;
        position: relative;
        transition:
          color var(--dur-feedback) var(--ease-standard),
          background-color var(--dur-feedback) var(--ease-standard);
      }

      .sb-tab i {
        font-size: 18px;
        flex-shrink: 0;
      }

      .sb-tab:hover {
        background: var(--surface-muted);
        color: var(--text-primary);
      }

      .sb-tab.active {
        background: none;
        color: var(--text-primary);
      }

      /* The accent dot marking the active screen, mirroring the mockup's
         .rail-link[aria-current]::before. */
      .sb-tab.active::before {
        content: '';
        position: absolute;
        left: 8px;
        top: 50%;
        width: 6px;
        height: 6px;
        margin-top: -3px;
        border-radius: 50%;
        background: var(--accent-ink);
      }

      .sb-footer {
        margin-top: auto;
        display: flex;
        flex-direction: column;
        gap: 2px;
      }

      .sb-footer-btn {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 40px;
        padding: 0 10px 0 22px;
        border: 0;
        border-radius: var(--r-button);
        background: none;
        cursor: pointer;
        color: var(--text-secondary);
        font-size: 14px;
        font-weight: 500;
        font-family: var(--font-sans);
        width: 100%;
        text-align: left;
        transition:
          color var(--dur-feedback) var(--ease-standard),
          background-color var(--dur-feedback) var(--ease-standard);
      }

      .sb-footer-btn:hover {
        background: var(--surface-muted);
        color: var(--text-primary);
      }

      .sb-footer-btn i {
        font-size: 18px;
        flex-shrink: 0;
      }

      /* Filled by renderRailNote() (Task 1.7): Worker version + index health. */
      .rail-note {
        margin: 18px 2px 0;
        padding-top: 16px;
        border-top: 1px solid var(--line);
        font-size: 12px;
        color: var(--text-secondary);
        line-height: 1.45;
      }

      .rail-note b {
        display: block;
        font-weight: 500;
        color: var(--text-primary);
      }

      /* ============ DESKTOP ============ */
      /* ── v2.3 Recent rows: title, meta, and a hidden destructive action ──
         Mobile-first. Actions are always present on touch (the overflow is the
         primary affordance); the hover reveal below is a desktop enhancement. */
      .card-title {
        font-weight: 500;
        font-size: 15px;
        line-height: 1.4;
        color: var(--text-primary);
        margin-bottom: 4px;
      }

      .card-preview {
        font-size: 13.5px;
        line-height: 1.5;
        color: var(--text-secondary);
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
      }

      .card-meta {
        display: flex;
        align-items: center;
        gap: 10px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 11px;
        color: var(--text-secondary);
        margin-bottom: 6px;
      }

      .card-source {
        display: inline-flex;
        align-items: center;
        gap: 4px;
      }

      .card-source i {
        font-size: 13px;
      }

      /* Tags scroll sideways rather than stacking into a tall wrap — a row's
         height should not depend on how many tags it carries.
         `flex-wrap: nowrap` is doing real work: the base rule above wraps, and
         inside the footer's narrow middle column that put every chip on its own
         line — a memory with nine tags became a nine-line row on a phone. */
      .card-tags {
        display: flex;
        flex-wrap: nowrap;
        gap: 6px;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        padding-bottom: 2px;
      }

      /* Phones give the tags their own full-width line. Squeezing them between
         the source badge and the action buttons leaves ~120px, which truncates
         chips mid-word ("second-brain-cl…") no matter how they wrap. */
      .card-footer {
        flex-wrap: wrap;
      }

      .card-meta {
        order: 1;
      }

      .card-actions {
        order: 2;
        margin-left: auto;
      }

      .card-tags {
        order: 3;
        flex: 1 0 100%;
      }

      .card-tags::-webkit-scrollbar {
        display: none;
      }

      .card-tags .tag-chip {
        flex: 0 0 auto;
      }

      .card-overflow {
        position: relative;
      }

      .overflow-btn {
        min-width: 44px;
        min-height: 44px;
        justify-content: center;
      }

      .card-overflow-menu {
        position: absolute;
        right: 0;
        bottom: calc(100% + 6px);
        z-index: 20;
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: 10px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
        padding: 4px;
        min-width: 200px;
      }

      .card-overflow-menu[hidden] {
        display: none;
      }

      .card-overflow-item {
        display: flex;
        align-items: center;
        gap: 8px;
        width: 100%;
        min-height: 44px;
        padding: 0 12px;
        background: none;
        border: 0;
        border-radius: 8px;
        font-size: 14px;
        color: var(--text-primary);
        cursor: pointer;
        text-align: left;
      }

      .card-overflow-item.danger {
        color: var(--danger);
      }

      .card-overflow-item.danger:hover {
        background: var(--danger-soft);
      }

      /* ── Safe areas ──────────────────────────────────────────────────────
         Installed as a PWA the app owns the whole screen, so the bottom bars
         sat under the iPhone home indicator. Paired with viewport-fit=cover. */
      .input-bar,
      .filter-bar {
        padding-bottom: max(env(safe-area-inset-bottom), 0px);
      }

      #menu-sheet .menu-inner,
      #integrations-sheet .menu-inner {
        padding-bottom: max(env(safe-area-inset-bottom), 16px);
      }

      @media (hover: hover) {
        .card-actions {
          opacity: 0;
          transition: opacity 0.15s ease;
        }

        .memory-card:hover .card-actions,
        .memory-card:focus-within .card-actions,
        .memory-card.card--menu-open .card-actions {
          opacity: 1;
        }
      }

      /* ── v2.3 memory detail: what the brain knows ─────────────────────── */
      .view-meta {
        display: flex;
        flex-wrap: wrap;
        gap: 12px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 11px;
        color: var(--text-secondary);
        margin-bottom: 14px;
        flex-shrink: 0;
      }

      .view-meta-item {
        display: inline-flex;
        align-items: center;
        gap: 4px;
      }

      .view-meta-item i {
        font-size: 13px;
      }

      .view-brain {
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: 12px;
        padding: 12px 14px;
        margin-bottom: 18px;
        flex-shrink: 0;
      }

      .view-brain-label {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10px;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--text-secondary);
        margin-bottom: 8px;
      }

      .view-brain-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        font-size: 13.5px;
        color: var(--text-secondary);
        padding: 3px 0;
      }

      .view-brain-row strong {
        color: var(--text-primary);
        font-weight: 500;
      }

      .view-brain-row .dots {
        color: var(--accent-ink);
        letter-spacing: 2px;
        font-size: 12px;
      }

      /* The sentence that explains a verdict, in the voice the rest of the
         product uses rather than the tag it is stored as. */
      .view-brain-note {
        font-family: var(--font-sans);
        font-size: 13px;
        line-height: 1.5;
        color: var(--text-secondary);
        margin-top: 6px;
      }

      .view-brain-note--warn {
        color: var(--warn);
        font-family: var(--font-sans);
      }

      /* Citation chips in an answer, and the card they point at. */
      .cite {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 18px;
        height: 18px;
        padding: 0 5px;
        margin: 0 2px;
        vertical-align: baseline;
        background: var(--accent-soft);
        color: var(--accent-ink);
        border: 0;
        border-radius: 5px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10.5px;
        line-height: 1;
        cursor: pointer;
      }

      .cite:hover {
        background: var(--accent);
        /* --on-accent, not a hardcoded white: --accent turns pale in dark
           theme, and white-on-pale-orange measures about 2:1. */
        color: var(--on-accent);
      }

      .memory-card--cited {
        box-shadow:
          0 0 0 2px var(--accent),
          var(--shadow-card);
        transition: box-shadow 0.3s ease;
      }

      /* Capture receipt — the site's "● stored to brain" moment. */
      .receipt {
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: 12px;
        padding: 12px 14px;
        margin: 8px 0 4px;
      }

      .receipt-headline {
        display: flex;
        align-items: center;
        gap: 8px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 11.5px;
        color: var(--accent-ink);
      }

      .receipt-dot {
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: var(--accent);
        flex-shrink: 0;
      }

      .receipt-filed {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 6px;
        margin-top: 10px;
        font-size: 13px;
        color: var(--text-secondary);
      }

      .receipt-note {
        font-family: var(--font-serif);
        font-style: italic;
        font-size: 13.5px;
        line-height: 1.5;
        color: var(--text-secondary);
        margin-top: 8px;
      }

      /* The number the answer cites this source by. Same visual language as the
         inline chip, so [2] in the prose and 2 on the card read as one thing. */
      .cite-badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 18px;
        height: 18px;
        padding: 0 5px;
        background: var(--accent-soft);
        color: var(--accent-ink);
        border-radius: 5px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10.5px;
        line-height: 1;
        flex-shrink: 0;
      }

      /* A scrolling tag row clipped hard at the card edge reads as a rendering
         fault; the fade says "there is more this way". Harmless when the tags
         already fit, because then it only fades empty space. */
      .card-tags {
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
        mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
      }

      /* ── Sheet proportions ────────────────────────────────────────────────
         The memory is the point of this sheet, and it was losing: three rows of
         tags, the verdict panel and three related cards left the text about
         four lines in a 90vh sheet. The content now takes the space and the
         supporting sections are capped. */
      /* The middle of the sheet scrolls as one, so the memory renders at its
         natural length instead of through a keyhole while the panels below it
         each keep their own scrollbar — and, more importantly, so nothing can
         push the action row past the sheet's own padding. */
      .view-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        /* Room for the fixed action row below, which would otherwise sit right
           on top of the last line of text as it scrolls under. */
        padding-bottom: 4px;
      }

      .view-content {
        flex: 0 0 auto;
        overflow-y: visible;
      }

      /* One scrolling line, like the tag rows on cards — a memory with a dozen
         tags should not cost three lines of the reading area. */
      .view-tags {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
        mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
        margin-bottom: 14px;
      }

      .view-tags::-webkit-scrollbar {
        display: none;
      }

      .view-tags .tag-chip {
        flex: 0 0 auto;
      }

      /* Related is context, not the subject: it scrolls within its own bounds
         rather than pushing the memory off the top of the sheet. */
      /* No longer its own scroll area: the region above scrolls, and a panel
         that scrolled inside a scrolling panel meant two gestures to read one
         memory. */
      .view-related {
        flex-shrink: 0;
      }

      /* Related memories: open rows on a hairline, not boxes. This list was
         previously unstyled entirely (browser defaults). */
      .view-related-label {
        font-size: 11px;
        color: var(--text-secondary);
        text-transform: uppercase;
        letter-spacing: 0.06em;
        font-weight: 500;
        margin-bottom: 6px;
      }

      .related-item {
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 10px 0;
        border-top: 1px solid var(--line);
      }

      .related-item:first-of-type {
        border-top: none;
      }

      .related-open {
        flex: 1;
        min-width: 0;
        text-align: left;
        background: none;
        border: none;
        cursor: pointer;
        font-family: var(--font-sans);
        font-size: 13.5px;
        color: var(--text-primary);
        line-height: 1.4;
      }

      .related-type {
        display: block;
        font-size: 11.5px;
        color: var(--text-secondary);
        margin-bottom: 2px;
      }

      .related-unlink {
        flex-shrink: 0;
        width: 30px;
        height: 30px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: none;
        border: none;
        border-radius: var(--r-button);
        color: var(--text-secondary);
        cursor: pointer;
        transition: background-color var(--dur-feedback) var(--ease-standard), color var(--dur-feedback) var(--ease-standard);
      }

      .related-unlink:hover {
        background: var(--danger-soft);
        color: var(--danger);
      }

      /* ── v2.3 daily brief ─────────────────────────────────────────────────
         The home surface. Quiet by construction: when nothing happened it does
         not render, and the welcome hero stands in its place. */
      /* Explicit separation from the home block above. #brief is a sibling of
         #home, not a child, and the two were previously kept apart only by
         whatever slack happened to be left at the bottom of #home — which is
         how "YOUR BRAIN, LATELY" ended up sitting on the tag chips on iOS. */
      .brief {
        padding: 4px 0 8px;
        margin-top: 20px;
      }

      .brief-eyebrow {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10px;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        color: var(--accent-ink);
        margin-bottom: 14px;
      }

      .brief-card {
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: 12px;
        padding: 12px 14px;
        margin-bottom: 10px;
      }

      /* Nothing to decide here — context, not a prompt for action. */
      .brief-card--quiet {
        background: none;
        border-style: dashed;
      }

      .brief-label {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10px;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--text-secondary);
        margin-bottom: 6px;
      }

      .brief-body {
        font-family: var(--font-serif);
        font-size: 15px;
        line-height: 1.55;
        color: var(--text-primary);
      }

      .brief-actions {
        display: flex;
        gap: 8px;
        margin-top: 10px;
      }

      /* Sits in the same list as the cards above it, so it takes the same
         block rhythm rather than the inline width a bare <button> defaults to. */
      .brief-more {
        display: block;
        margin: 0 0 10px;
      }

      .brief-suggestions {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin-top: 14px;
      }

      /* ── Brief dashboard panels ───────────────────────────────────────── */
      .brief-grid {
        display: grid;
        grid-template-columns: 1fr;
        gap: 10px;
        margin-bottom: 12px;
      }

      .brief-panel {
        background: var(--bg-card);
        border: 1px solid var(--border-card);
        border-radius: 12px;
        padding: 12px 14px;
      }

      /* Activity strip. No axes: the shape is the point, and the counts are in
         the tooltips. */
      .spark {
        display: flex;
        align-items: flex-end;
        gap: 3px;
        height: 46px;
        margin-top: 4px;
      }

      .spark-bar {
        flex: 1 1 0;
        min-width: 0;
        background: var(--accent);
        border-radius: 2px 2px 0 0;
        opacity: 0.85;
      }

      /* A day with nothing is a fact worth seeing, so it keeps its slot. */
      .spark-bar--empty {
        background: var(--surface-3);
        opacity: 1;
      }

      .src-row {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-top: 7px;
        font-size: 12.5px;
        color: var(--text-secondary);
      }

      .src-name {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        min-width: 88px;
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 11px;
      }

      .src-bar {
        flex: 1;
        height: 6px;
        background: var(--surface-2);
        border-radius: 3px;
        overflow: hidden;
      }

      .src-fill {
        display: block;
        height: 100%;
        background: var(--accent);
        opacity: 0.8;
      }

      .src-n {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 11px;
        color: var(--text-secondary);
        min-width: 22px;
        text-align: right;
      }

      .topic-chips {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        margin-top: 8px;
      }

      .topic-chip {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        background: var(--bg-tag);
        color: var(--text-tag);
        border: 0;
        border-radius: var(--radius-tag);
        padding: 5px 10px;
        font-size: 12px;
        font-family: var(--font-sans);
        cursor: pointer;
      }

      .topic-chip span {
        font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 10px;
        /* --text-tertiary does not clear AA on --bg-tag with the
           living-thread palette (only on --bg-card); --text-secondary does. */
        color: var(--text-secondary);
      }

      .topic-chip:hover {
        background: var(--accent-soft);
        color: var(--accent-ink);
      }

      .brief-attention {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin-bottom: 14px;
      }

      /* The neutral outlined chip from the mockup, not a peach/gold pill,
         used by both the board's decisions panel (board.js) and the older
         #brief fallback (brief.js) it replaces, so the two read the same. */
      .attn {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        height: 32px;
        background: var(--bg-card);
        border: 1px solid var(--line);
        border-radius: var(--r-chip-outline);
        padding: 0 12px 0 10px;
        font-size: 13.5px;
        color: var(--text-primary);
        cursor: pointer;
        font-family: var(--font-sans);
      }

      .attn i {
        color: var(--text-secondary);
        font-size: 16px;
      }

      .attn:hover {
        background: var(--surface-muted);
      }

      /* ── Home: dashboard header + command-bar composer ──────────────────
         The old layout centred a hero and an input in a trench of empty
         space. The home screen is a command bar and a board now: the header
         sits at the top of normal flow, and the board (board.css) follows. */
      .home {
        padding: 0 0 8px;
        text-align: left;
      }

      .home-head {
        display: flex;
        flex-direction: column;
        gap: 6px;
      }

      .home-greet {
        display: flex;
        align-items: center;
        gap: 12px;
        font-family: var(--font-display);
        font-size: 32px;
        font-weight: 500;
        letter-spacing: -0.03em;
        color: var(--text-primary);
        margin: 0;
      }

      .home-mark {
        color: var(--accent);
        font-size: 24px;
        display: inline-flex;
      }

      .home-sub {
        font-size: 15px;
        color: var(--text-secondary);
        margin: 0;
      }

      /* The command bar. One row: textarea, the mode guess, the hashtag hint,
         the send button, a single decision surface rather than a box with a
         toolbar underneath it. */
      .composer {
        display: flex;
        align-items: center;
        gap: 12px;
        min-height: 64px;
        margin-top: 20px;
        padding: 10px 10px 10px 20px;
        background: var(--bg-card);
        border: 1px solid var(--border-input);
        border-radius: var(--r-panel);
        box-shadow: var(--shadow-card);
        transition: border-color var(--dur-feedback) var(--ease-standard);
      }

      .composer:focus-within {
        border-color: var(--text-primary);
      }

      .home-field {
        flex: 1;
        min-width: 0;
        border: 0;
        background: none;
        resize: none;
        outline: none;
        font-family: var(--font-sans);
        font-size: 16px;
        line-height: 1.5;
        color: var(--text-primary);
        max-height: 40vh;
      }

      .home-field::placeholder {
        color: var(--text-secondary);
      }

      /* The prediction, always visible and always one tap from being overridden.
         Hidden only while the field is empty, when there is nothing to predict. */
      .home-mode {
        display: inline-flex;
        align-items: center;
        gap: 7px;
        flex-shrink: 0;
        height: 32px;
        background: none;
        border: 1px solid var(--border-soft);
        border-radius: var(--r-chip-outline);
        padding: 0 10px;
        font-family: var(--font-sans);
        font-size: 12px;
        font-weight: 500;
        color: var(--text-primary);
        white-space: nowrap;
        cursor: pointer;
        visibility: hidden;
        transition:
          background-color var(--dur-state) var(--ease-standard),
          border-color var(--dur-state) var(--ease-standard);
      }

      .home-mode:hover {
        background: var(--surface-muted);
      }

      .home-mode-dot {
        width: 5px;
        height: 5px;
        border-radius: 50%;
        background: var(--accent-ink);
      }

      .home-mode--remember {
        background: var(--surface-muted);
        border-color: transparent;
      }

      .home-mode--remember .home-mode-dot {
        background: var(--text-primary);
      }

      /* Add #tags anywhere, shown inline in the bar at desktop widths. */
      .hint {
        flex-shrink: 0;
        font-size: 13px;
        color: var(--text-secondary);
        white-space: nowrap;
      }

      .hint .hashtag,
      .home-hint-mobile .hashtag {
        color: var(--accent-ink);
        font-style: italic;
      }

      /* #screen-home scoped: #screen-home .send-btn above (line ~1026) is an ID
         selector and would otherwise win over a plain `.composer .send-btn`,
         leaving the composer's button transparent instead of the action-orange
         square the mockup shows. */
      #screen-home .composer .send-btn {
        width: 42px;
        height: 42px;
        flex-shrink: 0;
        border-radius: var(--r-button);
        background: var(--action);
      }

      #screen-home .composer .send-btn:hover {
        background: var(--action-hover);
      }

      #screen-home .composer .send-btn i {
        /* --on-accent, not a hardcoded white: --action goes pale in dark
           theme, and white-on-pale-orange measures about 2:1. */
        color: var(--on-accent);
      }

      /* The receipt from the last capture, one line under the bar. */
      .home-more {
        margin-top: 10px;
      }

      /* With nothing to show this element is zero-height, and its margin was
         the only thing holding the brief off the tag chips. Margin on an empty
         box is not spacing anyone declared — it is leftover, and an engine that
         lays it out differently drops the two sections onto each other. */
      .home-more:empty {
        margin-top: 0;
      }

      /* On the home screen the old bottom bars are redundant: the composer
         above is the input, and a tag filter with nothing to filter is
         furniture. The suggestions row moved inside #home (the .quick group
         below), so it is not in this list. */
      #screen-home.home-visible .input-bar,
      #screen-home.home-visible .filter-bar {
        display: none;
      }

      /* The hashtag lesson repeated below the bar for widths where .hint
         (inline in the composer) is hidden. Hidden by default; the 900px
         breakpoint below swaps the two. */
      .home-hint-mobile {
        display: none;
        font-size: 12.5px;
        color: var(--text-secondary);
        margin: 8px 0 0;
      }

      @media (max-width: 899px) {
        .hint {
          display: none;
        }
        .home-hint-mobile {
          display: block;
        }
      }

      .home-layer-hint {
        font-size: 12px;
        color: var(--text-secondary);
        margin-top: 8px;
      }

      /* Topic chips and suggestions share one row: "Ask about" plus what the
         brain has actually been fed, then a divider, then the recall prompts
         that used to appear only once a conversation had started. */
      .quick {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 8px;
        margin-top: 14px;
      }

      .quick-label {
        font-size: 13px;
        color: var(--text-secondary);
        margin-right: 2px;
      }

      .home-topics {
        display: contents;
      }

      .quick-sep {
        width: 1px;
        height: 20px;
        background: var(--line);
        margin: 0 2px;
      }

      .quick .suggestions-row {
        flex: 1 0 100%;
        padding: 0;
        margin-top: 2px;
      }

      @media (max-width: 767px) {
        .quick-sep {
          display: none;
        }
        .composer {
          flex-wrap: wrap;
          padding: 14px 10px 10px 16px;
        }
        .home-field {
          flex: 1 0 100%;
        }
        .composer .send-btn {
          margin-left: auto;
        }
      }

      /* First-run coach marks (js/coach.js). An inline callout under the
         control it explains, not a floating popover — see that file for why.
         The 32px tap target is on the base rule rather than in the phone
         media query, because the mark is the same size at every width. */
      .coach-mark {
        margin-top: 10px;
        padding: 12px 14px;
        border: 1px solid var(--line);
        border-radius: var(--r-card);
        background: var(--surface-muted);
      }

      .coach-mark-title {
        font-size: 13.5px;
        font-weight: 600;
        color: var(--text-primary);
        margin-bottom: 4px;
      }

      .coach-mark-body {
        margin: 0;
        font-size: 12.5px;
        line-height: 1.5;
        color: var(--text-secondary);
      }

      .coach-mark-dismiss {
        /* 31px wide before this: a hair under the 32px minimum, and the one
           control on the card a thumb is most likely to reach for. */
        min-width: 32px;
        margin-top: 8px;
        min-height: 32px;
        padding: 0;
        background: none;
        border: none;
        font-family: var(--font-sans);
        font-size: 12.5px;
        color: var(--text-secondary);
        text-decoration: underline;
        cursor: pointer;
      }

      @media (min-width: 768px) {
        .brief-grid {
          grid-template-columns: repeat(3, 1fr);
        }
      }

      @media (min-width: 768px) {
        /* Wide enough for one row again: badge, tags, actions. */
        .card-footer {
          flex-wrap: nowrap;
        }

        .card-tags {
          order: 2;
          flex: 1 1 auto;
        }

        .card-actions {
          order: 3;
          margin-left: 0;
        }
      }

      @media (min-width: 768px) {
        html,
        body {
          height: 100%;
          overflow: auto;
        }

        body {
          background: var(--surface-3);
        }

        #auth-overlay {
          position: fixed;
          inset: 0;
          max-width: none;
          background: color-mix(in srgb, var(--bg) 88%, transparent);
          backdrop-filter: blur(8px);
          align-items: center;
          justify-content: center;
          padding: 0;
        }

        .auth-card {
          background: var(--bg);
          border-radius: var(--r-panel);
          padding: 44px 38px;
          width: 420px;
          box-shadow: var(--shadow-float);
          display: flex;
          flex-direction: column;
          align-items: center;
        }

        #app {
          flex-direction: row;
          height: 100vh;
          max-width: 100%;
          width: 100%;
          border-radius: 0;
          overflow: hidden;
        }

        #sidebar {
          display: flex;
        }
        #topbar {
          display: none;
        }
        #navbar {
          display: none;
        }

        #main-panel {
          margin: 0 auto;
          border-left: 0.5px solid var(--border);
          border-right: 0.5px solid var(--border);
        }

        #confirm-dialog,
        #append-sheet,
        #edit-sheet,
        #menu-sheet,
        #integrations-sheet,
        #patterns-sheet,
        #stale-sheet,
        #view-sheet {
          align-items: center;
          justify-content: center;
        }

        .confirm-sheet,
        .append-inner,
        .edit-inner,
        .menu-inner,
        .view-inner {
          border-radius: var(--r-panel);
          max-width: 420px;
          transform: translateY(16px) scale(0.98);
          /* Bottom-sheet shadow points upward; on centered modals it reads as a
             flat dark bar under the panel. Use the floating elevation instead. */
          box-shadow: var(--shadow-float);
          border: 0.5px solid var(--border-card);
        }

        /* The editor is the exception to the 420px modal width. Every other
           sheet presents something to read or a short list to pick from; this
           one is a writing surface, and 420px on a 1,500px window meant editing
           a long memory through a column narrower than the row it came from. */
        .edit-inner {
          max-width: 680px;
        }

        .open .confirm-sheet,
        .open .append-inner,
        .open .edit-inner,
        .open .menu-inner,
        .open .view-inner {
          transform: none;
        }
      }

      ::-webkit-scrollbar {
        width: 0;
      }

      /* forget animation */
      @keyframes explode-out {
        0% {
          transform: scale(1);
          opacity: 1;
          filter: brightness(1) blur(0);
        }
        20% {
          transform: scale(1.04) translateY(-2px);
          opacity: 1;
          filter: brightness(1.4) blur(1px);
        }
        100% {
          transform: scale(0.6) translateY(20px);
          opacity: 0;
          filter: brightness(2.2) blur(8px);
        }
      }

      .explode-out {
        /* Was a hand-picked overshoot curve; --ease-exit is the system's
           token for exits and collapses (no overshoot in the timing
           function itself, per DESIGN.md). */
        animation: explode-out 0.4s var(--ease-exit) forwards !important;
        pointer-events: none;
      }

      @media (prefers-reduced-motion: reduce) {
        *,
        *::before,
        *::after {
          animation-duration: 0.01ms !important;
          transition-duration: 0.05s !important;
        }
      }

      /* Download-the-app CTA (sidebar footer).
         A solid pill rather than another nav row like Settings — it is the one
         thing here aimed at someone who has not installed the app yet.
         .sb-footer-btn was written for <button>, so as an <a> it also needs the
         underline removed. */
      #sb-download-app {
        --dl-bg: #1f2532;
        --dl-fg: #ffffff;
        box-sizing: border-box;
        justify-content: center;
        gap: 9px;
        margin-bottom: 10px;
        padding: 11px 14px;
        border: none;
        border-radius: var(--radius-pill);
        background: var(--dl-bg);
        color: var(--dl-fg);
        font-weight: 650;
        /* "Download for Windows" is the longest label and wraps to two lines in
           a narrow sidebar, which reads as broken rather than tight. */
        white-space: nowrap;
        font-size: 14px;
        text-decoration: none;
        box-shadow: 0 1px 2px rgba(22, 22, 22, 0.14);
        transition:
          background 0.15s,
          box-shadow 0.15s,
          transform 0.08s;
      }

      /* Inverted in dark mode: the light-mode fill is nearly --bg there, so the
         button would disappear into the sidebar. */
      html[data-theme='dark'] #sb-download-app {
        --dl-bg: var(--text-primary);
        --dl-fg: #161616;
        box-shadow: none;
      }

      #sb-download-app i {
        color: inherit;
        font-size: 17px;
      }

      #sb-download-app:hover {
        background: #2a3444;
        color: var(--dl-fg);
        box-shadow: 0 2px 6px rgba(22, 22, 22, 0.18);
      }

      html[data-theme='dark'] #sb-download-app:hover {
        background: #ffffff;
      }

      /* A press that visibly depresses is most of what makes a link feel like a
         button. */
      #sb-download-app:active {
        transform: translateY(1px);
        box-shadow: 0 1px 1px rgba(22, 22, 22, 0.16);
      }

      /* ── Team panel (public/js/team.js) ─────────────────────────────
         A roster that reads like a table without being one: hairline
         rows in a single rounded container, an aligned header, and
         quiet icon actions that only speak on hover. */

      /* Present to a screen reader, invisible on screen. Used for the roster
         table's <caption> and the token-ready status line: content that
         orients or announces, with nothing to draw. */
      .sr-only {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
      }

      .team-page-bar h2 {
        margin: 0;
        font-family: var(--font-display);
        font-size: clamp(24px, 3vw, 34px);
        font-weight: 500;
        letter-spacing: -0.03em;
      }

      .team-content {
        flex: 1;
        overflow-y: auto;
        padding: 24px 28px;
      }

      .team-panels {
        display: flex;
        flex-direction: column;
        gap: 20px;
      }

      .team-panel { padding: 20px 22px 22px; }
      .team-panel > h2 { margin-bottom: 14px; }
      .team-open-row,
      .team-actions-row {
        display: flex;
        align-items: center;
        gap: 10px;
        flex-wrap: wrap;
      }
      .team-open-row { padding: 10px 0; border-bottom: 1px solid var(--line); }
      .team-open-row i { color: var(--text-secondary); }
      .team-open-row > span { color: var(--text-secondary); font-size: 13px; }
      .team-actions-row code { font-size: 12px; overflow-wrap: anywhere; }
      .team-add-fields { flex-wrap: wrap; }
      #team-add-role { flex: 0 1 auto; }
      .team-activity-actions { margin-top: 10px; }
      .activity-filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }
      /* Selected state, exactly .seg's (board.css): charcoal fill, white text.
         Each filter is a standalone .btn-secondary rather than a segment
         inside one shared track, so its own border is pulled to match rather
         than left as a lighter ring around the darker fill. */
      .activity-filters button[aria-checked='true'] {
        background: var(--text-primary);
        border-color: var(--text-primary);
        color: var(--on-accent);
      }
      /* Team name editor. Matches the org-default pill above the roster: same
         border, radius and surface, so the two settings rows read as a pair. */
      .team-name-row {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 10px 14px;
        border: 1px solid var(--border-input);
        border-radius: 14px;
        background: var(--surface-2);
      }

      .team-name-icon {
        /* --text-tertiary does not clear AA on the row's --surface-2
           background with the living-thread palette; --text-secondary does. */
        color: var(--text-secondary);
        font-size: 13px;
        flex-shrink: 0;
      }

      .team-name-input {
        flex: 1 1 auto;
        min-width: 0;
      }

      .team-name-note {
        margin: 8px 2px 0;
      }

      .team-table {
        /* Panel vocabulary, not the settings-row one: 1px var(--line) and the
           board's 14px radius, no --surface-2 box around it. */
        border: 1px solid var(--line);
        border-radius: 14px;
        overflow: hidden;
        background: var(--surface);
      }

      /* The two single-setting boxes (team name, your capture default,
         renderTeamMember) and the member's own read-only roster
         (teamRosterRow) reuse .team-table/.team-row as a plain grid row,
         not the real <table> the ADMIN roster below renders (renderTeam),
         which gets its own tag-scoped rules further down. Scoped to `div`
         so it cannot also match the roster's <tr class="team-row">: a grid
         display on a table row breaks the table it sits in. */
      div.team-row {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto auto;
        gap: 16px;
        align-items: center;
        padding: 10px 14px;
      }

      div.team-row + div.team-row {
        border-top: 1px solid var(--border-input);
      }

      .team-row.suspended .team-name {
        color: var(--text-secondary);
      }

      /* The admin member roster: a real <table> (renderTeam/teamMemberRow,
         js/team.js), not a div roster, so a screen reader gets row/column
         semantics instead of a flat wall of spans. */
      table.team-table {
        border-collapse: collapse;
        width: 100%;
      }

      table.team-table th {
        font-size: 10.5px;
        font-weight: 500;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        /* --text-tertiary does not clear AA on --surface-2 with the
           living-thread palette (only on --bg-card); --text-secondary does. */
        color: var(--text-secondary);
        background: var(--surface-2);
        text-align: left;
        padding: 10px 14px;
        border-bottom: 1px solid var(--border-input);
      }

      table.team-table td {
        padding: 10px 14px;
        vertical-align: middle;
        border-top: 1px solid var(--border-input);
      }

      table.team-table tbody tr:first-child td {
        border-top: 0;
      }

      .team-name {
        display: flex;
        align-items: center;
        gap: 6px;
        flex-wrap: wrap;
        min-width: 0;
      }

      .team-sub {
        font-size: 11.5px;
        color: var(--text-secondary);
        margin-top: 2px;
      }

      .team-actions {
        display: flex;
        align-items: center;
        gap: 2px;
      }

      .team-icon-btn {
        border: none;
        background: none;
        padding: 6px;
        border-radius: 8px;
        cursor: pointer;
        color: var(--text-secondary);
        font-size: 15px;
        line-height: 1;
        display: inline-flex;
        transition: background 0.15s, color 0.15s;
      }

      .team-icon-btn:hover {
        background: var(--surface-2);
        color: var(--text-secondary);
      }

      .team-icon-btn.danger:hover {
        background: var(--danger-soft);
        color: var(--danger);
      }

      /* Team panel dropdowns: native select behaviour, dressed like the rest
         of the panel. The chevron is a sibling icon (not a background image)
         so it follows the theme's text color in light and dark mode. */
      .team-select-wrap {
        position: relative;
        display: inline-flex;
        align-items: center;
      }

      .team-select-wrap select {
        appearance: none;
        -webkit-appearance: none;
        font-size: 12px;
        font-weight: 500;
        color: var(--text-secondary);
        background: var(--surface);
        border: 1px solid var(--border-input);
        border-radius: 9px;
        padding: 5px 26px 5px 11px;
        cursor: pointer;
        transition: border-color 0.15s, background 0.15s;
      }

      .team-select-wrap select:hover {
        border-color: var(--text-secondary);
      }

      .team-select-wrap select:focus {
        outline: none;
        border-color: var(--accent);
      }

      .team-select-wrap .ti {
        position: absolute;
        right: 9px;
        font-size: 12px;
        color: var(--text-secondary);
        pointer-events: none;
        transition: color 0.15s;
      }

      .team-select-wrap select:hover + .ti {
        color: var(--text-secondary);
      }

      /* On/off switch, for a setting that is genuinely binary — a two-option
         <select> makes the reader open a menu to find out there was nothing to
         choose. Built on a real <input type="checkbox"> so it is focusable,
         Space-toggles, and is announced as a checkbox; the input is made
         TRANSPARENT rather than display:none or visibility:hidden, both of
         which would take it out of the tab order along with the pixels.
         Sits alongside .team-select-wrap and borrows its language: the same
         --border-input hairline, the same --accent focus boundary. */
      .sb-switch {
        position: relative;
        display: inline-flex;
        align-items: center;
        /* A base rule, not a phone-only one: the pointer that most often misses
           a 22px control is a thumb, and this row is reachable at every width. */
        min-height: 32px;
        flex-shrink: 0;
        cursor: pointer;
      }

      .sb-switch-input {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        margin: 0;
        opacity: 0;
        cursor: inherit;
      }

      .sb-switch-track {
        width: 40px;
        height: 22px;
        padding: 2px;
        display: inline-flex;
        align-items: center;
        border-radius: var(--radius-pill);
        background: var(--surface-3);
        border: 1px solid var(--border-input);
        /* The input is on top; without this the track would eat its own clicks
           in browsers that hit-test the sibling first. */
        pointer-events: none;
        transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
      }

      .sb-switch-knob {
        width: 17px;
        height: 17px;
        border-radius: 50%;
        background: var(--switch-knob);
        border: 1px solid var(--border-input);
        box-shadow: var(--shadow-card);
        transition: transform 0.18s var(--ease);
      }

      /* --accent as a FILL and as a boundary, never as ink: nothing is
         written on the track. */
      .sb-switch-input:checked + .sb-switch-track {
        background: var(--accent);
        border-color: var(--accent);
      }

      .sb-switch-input:checked + .sb-switch-track .sb-switch-knob {
        /* 40 track - 4 padding - 1 border - 17 knob. */
        transform: translateX(18px);
      }

      /* Focus lands on the invisible input, so the ring has to be drawn on the
         track. :focus-visible rather than :focus, so a pointer tap does not
         leave a ring sitting there. */
      .sb-switch-input:focus-visible + .sb-switch-track {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
      }

      /* Locked on by real membership. Dimmed and not-allowed rather than
         removed: a control that vanishes leaves the reader wondering where the
         setting went, and the hint beside it explains the lock. */
      .sb-switch-input:disabled {
        cursor: not-allowed;
      }

      .sb-switch-input:disabled + .sb-switch-track {
        opacity: 0.55;
      }

      @media (prefers-reduced-motion: reduce) {
        .sb-switch-track,
        .sb-switch-knob {
          transition: none;
        }
      }

      .team-capture-label {
        display: flex;
        align-items: center;
        gap: 7px;
        font-size: 11.5px;
        color: var(--text-secondary);
        white-space: nowrap;
      }

      .app-toast {
        position: fixed;
        bottom: 24px;
        left: 50%;
        transform: translateX(-50%) translateY(12px);
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 10px 14px;
        background: var(--surface-elevated);
        border: 1px solid var(--border);
        border-radius: 10px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.2s, transform 0.2s;
        z-index: 9999;
        max-width: min(90vw, 420px);
      }

      .app-toast.visible {
        opacity: 1;
        pointer-events: auto;
        transform: translateX(-50%) translateY(0);
      }

      .app-toast-msg {
        font-size: 13px;
        color: var(--text-on-elevated);
      }

      .app-toast-action {
        background: none;
        border: none;
        color: var(--accent-on-elevated);
        font-size: 13px;
        font-weight: 600;
        cursor: pointer;
        padding: 0;
        white-space: nowrap;
      }

      .view-timeline {
        margin-top: 12px;
        padding-top: 12px;
        border-top: 1px solid var(--border);
      }

      .view-timeline-label {
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        color: var(--text-secondary);
        margin-bottom: 8px;
      }

      .view-timeline-item {
        font-size: 12px;
        color: var(--text-secondary);
        padding: 4px 0;
      }

      /* Why the two view buttons below are unavailable. Tertiary ink: it is a
         footnote on the history, not another entry in it. */
      .view-timeline-note {
        font-size: 12px;
        color: var(--text-secondary);
        padding: 6px 0 0;
      }

      /* Dimming rather than a colour change — the view buttons already vary
         colour by role (.btn-view-forget carries --danger), and overriding that
         would lose the role while trying to say "unavailable". The list card's
         locked controls share the rule rather than a second one: they are the
         same three buttons over the same row, and two rules would let the two
         surfaces look different about the same fact. */
      .view-btn--locked,
      .card-action-btn--locked {
        opacity: 0.45;
        cursor: not-allowed;
      }

      /* ── Phone-width overrides ─────────────────────────────────────
         Last in the file on purpose. A media query adds no specificity,
         so these have to come after every base rule they override or the
         base rule simply wins — which is what happened when this block
         sat above .team-row and its three-column grid went on applying at
         390px, laying the columns over each other. */
      /* Touch targets on phone-width layouts.
         This stylesheet is mobile-first, so these are the sizes a phone actually
         gets; desktop keeps its own via the min-width:768px blocks above. The
         home topic chips, the layer select and the send arrow all measured
         26-31px tall at 390px wide, which is under every published minimum and
         noticeably fiddly on the one screen where the composer is the whole
         point. Raised to 32px rather than the 44px Apple suggests, because these
         sit in dense rows and 44 would push the composer below the fold — 32 is
         the point at which mis-taps stop, without redesigning the screen. */
      @media (max-width: 767px) {
        .topic-chip,
        .team-select-wrap select,
        .nav-tab {
          min-height: 32px;
        }

        #screen-home .send-btn,
        #screen-recall .send-btn {
          min-width: 32px;
          min-height: 32px;
          display: inline-flex;
          align-items: center;
          justify-content: center;
        }

        /* .filter-field carries no padding of its own — it is a bare input
           inside the 36px .filter-inner rectangle, so the rectangle looks like
           the control while the thing that actually takes the tap is one line
           tall (17px). Filling it costs nothing visually and makes the hit
           area match what the eye already believes it is aiming at. */
        .filter-field {
          min-height: 32px;
        }

        .digest-btn,
        .view-toggle-btn {
          min-height: 32px;
        }

        .team-icon-btn {
          min-width: 32px;
          min-height: 32px;
        }

        /* The two single-setting boxes (team name, your capture default) and
           the member's own read-only roster are still a plain grid row at
           this width — a three-column grid demands more width than a phone
           has, so they were laid over the first column instead of wrapping.
           Stack it. The admin roster's real <table> gets its own stacking
           rules below (max-width: 700px): it needs data-label, not just a
           narrower grid. */
        div.team-row {
          grid-template-columns: minmax(0, 1fr);
          gap: 10px;
          align-items: flex-start;
          padding: 14px;
        }

        .team-actions {
          justify-content: flex-start;
        }

        /* The Memories bar puts the filters and the list/graph toggle on one
           row. At 390px the toggle takes what it needs first and the filter pill
           is left with ~215px for three controls, so each select collapsed to
           40px and every one of them read "All" — the tag filter, the time
           filter and the layer filter became indistinguishable. Wrapping the bar
           gives the filters their own full-width row, which is the only way all
           three labels fit. */
        .mem-bar {
          flex-wrap: wrap;
        }

        .mem-filters {
          flex: 1 0 100%;
          max-width: none;
          order: 2;
        }
      }

      /* The admin roster table (renderTeam/teamMemberRow, js/team.js): at
         phone width four columns side by side no longer fit, so each row
         stacks and each cell shows its own header through a data-label
         attribute instead (the site's comparison-table technique). <thead>
         stays in the DOM and in the accessibility tree (visually hidden, not
         display:none), so a screen reader still gets the real column
         headers; only sighted users see them replaced by the per-cell label. */
      @media (max-width: 700px) {
        table.team-table thead {
          position: absolute;
          width: 1px;
          height: 1px;
          padding: 0;
          margin: -1px;
          overflow: hidden;
          clip: rect(0, 0, 0, 0);
          white-space: nowrap;
          border: 0;
        }

        table.team-table,
        table.team-table tbody,
        table.team-table tr,
        table.team-table td {
          display: block;
          width: 100%;
        }

        table.team-table tr.team-row {
          padding: 14px;
        }

        table.team-table tr.team-row + tr.team-row {
          border-top: 1px solid var(--border-input);
        }

        table.team-table td {
          padding: 6px 0;
          border-top: 0;
        }

        table.team-table td[data-label]::before {
          content: attr(data-label);
          display: block;
          font-size: 10.5px;
          font-weight: 500;
          letter-spacing: 0.08em;
          text-transform: uppercase;
          color: var(--text-secondary);
          margin-bottom: 4px;
        }
      }

      /* The admin activity feed. Rows are separated by a rule rather than by a
         card each: this is a record to be scanned top to bottom, and fifty
         cards read as fifty things to act on. --border, --text-primary and
         --text-tertiary are all defined in the token block above, in both the
         light and dark palettes; nothing here invents a token or carries a
         var() fallback for one that does not exist.

         No new tappable control lives here — #activity-more and
         #activity-export both reuse .digest-btn, which already carries the
         phone tap target. */
      .activity-row {
        padding: 9px 0;
        border-top: 1px solid var(--border);
      }

      .activity-row:first-child {
        border-top: none;
      }

      .activity-line {
        font-size: 12.5px;
        color: var(--text-primary);
      }

      .activity-when {
        font-size: 11.5px;
        color: var(--text-secondary);
        margin-top: 2px;
      }

      /* ── Memories multi-select ──────────────────────────────────────────
         The bar the Select button raises, and the per-card tick it puts on
         every row while the mode is on. Team brains only: renderBulkBar()
         (js/recent.js) holds both at display:none otherwise. */
      /* Matches .mem-bar directly above it — same gutter, same rule beneath —
         so the two read as one stack of controls rather than a strip that
         wandered in on top of the list. Without the gutter the count sat flat
         against the edge of a phone screen. */
      .mem-bulk-bar {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
        align-items: center;
        padding: 10px 18px;
        border-bottom: 1px solid var(--line);
      }

      /* Two kinds of button share this bar and they are not equivalent.
         Select all, Clear and Done change what is selected or leave the mode;
         Share with team and Make private MOVE memories between layers, and are
         the only controls here that touch data. Same size and same 32px tap
         target — the weight is what differs, so the pair that changes who can
         read a memory is not one of five identical pills. */
      .digest-btn--quiet {
        background: var(--surface-3);
        color: var(--text-secondary);
      }

      .digest-btn--quiet:hover {
        background: var(--surface-2);
      }

      .mem-bulk-count {
        font-size: 12.5px;
        color: var(--text-secondary);
      }

      /* The label, not the input, carries the tap target: a bare 13px checkbox
         is well under the 32px minimum, and growing the box itself would
         change how the control is drawn on every platform. */
      .card-select {
        min-height: 32px;
        min-width: 32px;
        display: inline-flex;
        align-items: center;
      }

      /* The native control is drawn by the OS, so it looked like a different
         product on every platform and ignored the palette entirely. Same shape
         language as the rest: the app's card radius, the app's border, and
         --accent as a FILL when on, never as ink. */
      .card-select input[type='checkbox'] {
        appearance: none;
        -webkit-appearance: none;
        /* The control draws no text, but an unset size leaves the browser's
           13.3333px default in the computed styles and in any audit of the
           page's type scale. */
        font-size: inherit;
        width: 18px;
        height: 18px;
        margin: 0;
        border: 1.5px solid var(--border-input);
        border-radius: 5px;
        background: var(--bg-card);
        cursor: pointer;
        display: grid;
        place-content: center;
        transition: background 0.15s var(--ease), border-color 0.15s var(--ease);
      }

      .card-select input[type='checkbox']::after {
        content: '';
        width: 10px;
        height: 10px;
        transform: scale(0);
        transition: transform 0.12s var(--ease);
        background: var(--switch-knob);
        clip-path: polygon(14% 44%, 0 60%, 39% 100%, 100% 18%, 84% 4%, 39% 68%);
      }

      .card-select input[type='checkbox']:checked {
        background: var(--accent);
        border-color: var(--accent);
      }

      .card-select input[type='checkbox']:checked::after {
        transform: scale(1);
      }

      .card-select input[type='checkbox']:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
      }

      /* In select mode the tick takes a reserved gutter on the card's own
         padding box rather than pushing the title down a line. */
      #recent-list .memory-card--selecting {
        position: relative;
        padding-left: 46px;
      }

      #recent-list .memory-card--selecting > .card-select {
        position: absolute;
        left: 14px;
        /* Optically centred on the title's first line rather than the card:
           a tall card would otherwise float its tick somewhere near the tags. */
        top: 14px;
        margin: 0;
        justify-content: center;
      }

      /* --accent as a BOUNDARY, never as ink. An outline (rather than a
         border) so a selected card does not shift by a pixel against its
         unselected neighbours. */
      .memory-card--selected {
        outline: 1px solid var(--accent);
        outline-offset: -1px;
      }

      /* ============ FOCUS ============
         One visible ring for every control, system-wide: 3px --accent-ink
         offset 4px. Last in the file so it wins the source-order tie against
         every earlier `outline: none` reset paired with a focus-time border
         or box-shadow substitute. Those stay as an extra cue, this is the
         one that is always there. */
      :is(a, button, input, select, textarea, [tabindex]):focus-visible {
        outline: 3px solid var(--accent-ink);
        outline-offset: 4px;
        border-radius: var(--r-button);
      }
