/* Master stylesheet — imports every layer in order.
   Order matters: tokens first, base next, then layout, then components. */

/* Design tokens — spacing, typography, radii, shadows, colors.
   Color structure:
     1. :root, [data-theme="light"]  — light (default + explicit)
     2. [data-theme="dark"]          — dark explicit
     3. @media prefers-color-scheme  — OS-preference catch-all (no data-theme) */


/* ─── Static tokens (theme-independent) ──────────────────────────────────── */

:root {
  /* Spacing */
  --sp-4xs: 2px;
  --sp-3xs: 4px;
  --sp-xxs: 6px;
  --sp-xs: 8px;
  --sp-sm: 12px;
  --sp-md: 16px;
  --sp-lg: 20px;
  --sp-xl: 24px;
  --sp-xxl: 32px;
  --sp-3xl: 40px;
  --sp-4xl: 48px;

  /* Sizing */
  --size-4xs: 2px;
  --size-3xs: 4px;
  --size-xxs: 6px;
  --size-xs: 8px;
  --size-sm: 12px;
  --size-md: 16px;
  --size-lg: 20px;
  --size-xl: 24px;
  --size-xxl: 32px;
  --size-3xl: 40px;
  --size-4xl: 48px;
  --size-5xl: 56px;

  /* Typography */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-serif: "New York", ui-serif, Georgia, serif;
  --text-xs: 11px;
  --text-sm: 13px;
  --text-base: 14px;
  --text-lg: 16px;
  --text-xl: 18px;
  --text-2xl: 22px;
  --text-3xl: 28px;
  --text-4xl: 32px;

  /* Radius */
  --radius-sm: 4px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 24px;
  --radius-full: 9999px;

  /* brand gradient */
  --fill-gradient:linear-gradient(180deg, #62D475 0%, #58BE68 100%);

  /* Pastel gradient — Please-agent "thinking" tint (timeline rows). */
  --gradient-pastel: linear-gradient(180deg, #EBBDF0 0%, #8FC2F7 49%, #61D475 100%);

  /* Accent color base rgb values to inject into RGB functions */
  --blue-1:       0, 145, 255;
  --blue-2:       0, 136, 255;
  --red-1:        255, 66, 69;
  --red-2:        255, 56, 60;
  --green-1:      48, 209, 88;
  --green-2:      52, 199, 89;
  --yellow-1:     255, 214, 0;
  --yellow-2:     255, 204, 0;
  --orange-1:     255, 146, 48;
  --orange-2:     255, 141, 40;
  --purple-1:     219, 52, 242;
  --purple-2:     203, 48, 224;

  --neutral-white:    255, 255, 255;
  --neutral-01:       242, 242, 247;
  --neutral-02:       58, 58, 60;
  --neutral-03:       44, 44, 46;
  --neutral-04:       28, 28, 30;
  --neutral-05:       13, 13, 13;
  --neutral-black:    0, 0, 0;
}


/* ─── Light theme — default + explicit ───────────────────────────────────── */

:root,
[data-theme="light"] {

  --blend-mode:      darken;

  --bg-base:               var(--neutral-white);
  --bg-base-inverse:       var(--neutral-black);

  --color-gray-1:          var(--neutral-01);
  --color-gray-2:          var(--neutral-02);
  --color-gray-3:          var(--neutral-03);
  --color-gray-4:          var(--neutral-04);
  --color-gray-5:          var(--neutral-05);

  --color-red:             var(--red-1);
  --color-orange:          var(--orange-1);
  --color-yellow:          var(--yellow-1);
  --color-green:           var(--green-1);
  --color-blue:            var(--blue-1);
  --color-purple:          var(--purple-1);

  --color-red-inverse:     var(--red-2);
  --color-orange-inverse:  var(--orange-2);
  --color-yellow-inverse:  var(--yellow-2);
  --color-green-inverse:   var(--green-2);
  --color-blue-inverse:    var(--blue-2);
  --color-purple-inverse:  var(--purple-2);

  /* Background */
  --bg-body:                       rgba(var(--bg-base), 1);
  --bg-primary:                    rgba(var(--bg-base), 1);
  --bg-secondary:                  rgba(var(--color-gray-1), 1);
  --bg-tertiary:                   rgba(var(--bg-base), .04);
  --bg-quarternary:                rgba(var(--bg-base), .02);

  --bg-elevated-primary:           rgba(var(--bg-base), 1);
  --bg-elevated-secondary:         rgba(var(--color-gray-1), 1);
  --bg-elevated-tertiary:          rgba(var(--bg-base), 1);

  --bg-body-inverse:               rgba(var(--color-gray-5), 1);
  --bg-primary-inverse:            rgba(var(--bg-base-inverse), 1);
  --bg-elevated-primary-inverse:   rgba(var(--color-gray-4), 1);
  --bg-elevated-secondary-inverse: rgba(var(--color-gray-3), 1);
  --bg-elevated-tertiary-inverse:  rgba(var(--color-gray-2), 1);    
  --bg-secondary-inverse:          rgba(var(--color-gray-4), 1);
  --bg-tertiary-inverse:           rgba(var(--color-gray-3), 1);
  --bg-quarternary-inverse:        rgba(var(--bg-base-inverse), .02);

  --bg-disabled:                   var(--bg-quarternary);
  --bg-disabled-inverse:           var(--bg-quarternary-inverse);
  --bg-0:                          rgba(var(--bg-base),0);
  --bg-70:                         rgba(var(--bg-base),.7);
  --bg-80:                         rgba(var(--bg-base),.8);
  --bg-70-inverse:                 rgba(var(--bg-base-inverse),.7);
  --bg-80-inverse:                 rgba(var(--bg-base-inverse),.8);

  /* Fill — base (solid) + elevated (translucent overlay) */
  --fill-primary:                     #CCCCCC;
  --fill-primary-inverse:             #333333;
  --fill-secondary:                   #E0E0E0;
  --fill-secondary-inverse:           #1F1F1F;
  --fill-tertiary:                    #EDEDED;
  --fill-tertiary-inverse:            #121212;
  --fill-elevated-primary:            rgba(120, 120, 120, 0.20);
  --fill-elevated-primary-inverse:    rgba(120, 120, 128, 0.36);
  --fill-elevated-secondary:          rgba(120, 120, 128, 0.16);
  --fill-elevated-secondary-inverse:  rgba(120, 120, 128, 0.32);
  --fill-elevated-tertiary:           rgba(118, 118, 128, 0.12);
  --fill-elevated-tertiary-inverse:   rgba(118, 118, 128, 0.24);
  --fill-elevated-quaternary:         rgba(116, 116, 128, 0.08);
  --fill-elevated-quaternary-inverse: rgba(118, 118, 128, 0.18);

  /* Brand gradients */
  --fill-gradient-green: linear-gradient(180deg, #62D475 0%, #58BE68 100%);

  /* Text */
  --text-primary:             #000000;
  --text-primary-inverse:     #FFFFFF;
  --text-secondary:           rgba(60,60,67,0.6);
  --text-secondary-inverse:   rgba(235,235,245,0.7);
  --text-tertiary:            rgba(60,60,67,0.3);
  --text-tertiary-inverse:    rgba(235,235,245,0.3);
  --text-quarternary:         rgba(60,60,67,0.18);
  --text-quarternary-inverse: rgba(235,235,245,0.16);
  --text-disabled:            var(--text-quarternary);
  --text-disabled-inverse:    var(--text-quarternary-inverse);

  --text-primary-vibrant:             #1A1A1A;
  --text-primary-vibrant-inverse:     #F5F5F5;
  --text-secondary-vibrant:           #727272;
  --text-secondary-vibrant-inverse:   #8A8A8A;
  --text-tertiary-vibrant:            #BFBFBF;
  --text-tertiary-vibrant-inverse:    #404040;
  --text-quarternary-vibrant:         #D9D9D9;
  --text-quarternary-vibrant-inverse: #262626;
  --text-disabled-vibrant:            var(--text-quarternary-vibrant);
  --text-disabled-vibrant-inverse:    var(--text-quarternary-inverse);

  /* Border */
  --border:           #e5e5e5;
  --border-strong:    #d4d4d4;
  --border-primary:   #C6C6C8;
  --border-secondary: rgba(0, 0, 0, 0.12);
  --border-tertiary:  rgba(0, 0, 0, 0.05);
  --border-vibrant:   #E6E6E6;

  /* Accent */
  --accent-blue:          rgba(var(--color-blue), 1);
  --accent-blue-inverse:  rgba(var(--color-blue-inverse), 1);
  --accent-cobalt:        #3b60e4;
  --accent-red:           rgba(var(--color-red), 1);
  --accent-red-inverse:   rgba(var(--color-red-inverse), 1);
  --accent-green:         rgba(var(--color-green), 1);
  --accent-green-inverse: rgba(var(--color-green-inverse), 1);
  --accent-yellow:        rgba(var(--color-yellow), 1);
  --accent-yellow-inverse:rgba(var(--color-yellow-inverse), 1);
  --accent-orange:        rgba(var(--color-orange), 1);
  --accent-orange-inverse:rgba(var(--color-orange-inverse), 1);
  --accent-purple:        rgba(var(--color-purple), 1);
  --accent-purple-inverse:rgba(var(--color-purple-inverse), 1);

  /* Timeline list (iOS parity) */
  --canvas-bg:         #F2F2F7;
  --tl-text-primary:   #000000;
  --tl-text-secondary: rgba(60, 60, 67, 0.6);
  --ui-borders:        #C6C6C8;
  --brand-blue:        #0088FF;
  --brand-blue-12:     rgba(0, 136, 255, 0.12);

  /* Shadow */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow:     0 2px 8px rgba(0, 0, 0, 0.06);

  /* Deprecated aliases — remove once consumers are migrated */
  --bg:         #ffffff;
  --bg-subtle:  #f5f5f4;
  --bg-elevated: #ffffff;
  --fg:         #0a0a0a;
  --fg-muted:   #737373;
  --fg-subtle:  #a3a3a3;
  --accent:     #0a0a0a;
  --accent-fg:  #ffffff;
  --danger:     #dc2626;

  color-scheme: light;
}


/* ─── Dark theme — explicit ───────────────────────────────────────────────── */

[data-theme="dark"] {

  --blend-mode:      lighten;

  --bg-base:                var(--neutral-black);
  --bg-base-inverse:        var(--neutral-white);

  --color-gray-1:           var(--neutral-01);
  --color-gray-2:           var(--neutral-02);
  --color-gray-3:           var(--neutral-03);
  --color-gray-4:           var(--neutral-04);
  --color-gray-5:           var(--neutral-05);

  --color-red:              var(--red-2);
  --color-orange:           var(--orange-2);
  --color-yellow:           var(--yellow-2);
  --color-green:            var(--green-2);
  --color-blue:             var(--blue-2);
  --color-purple:           var(--purple-2);

  --color-red-inverse:      var(--red-1);
  --color-orange-inverse:   var(--orange-1);
  --color-yellow-inverse:   var(--yellow-1);
  --color-green-inverse:    var(--green-1);
  --color-blue-inverse:     var(--blue-1);
  --color-purple-inverse:   var(--purple-1);

  /* Background */
  --bg-body:               rgba(var(--color-gray-5), 1);
  --bg-primary:            rgba(var(--bg-base), 1); 
  --bg-secondary:          rgba(var(--color-gray-4), 1);
  --bg-tertiary:           rgba(var(--color-gray-3), 1);
  --bg-quarternary:        rgba(var(--bg-base), .02);

  --bg-elevated-primary:   rgba(var(--color-gray-4), 1);
  --bg-elevated-secondary: rgba(var(--color-gray-3), 1);
  --bg-elevated-tertiary:  rgba(var(--color-gray-2), 1);   

  --bg-body-inverse:                       rgba(var(--bg-base-inverse), 1);
  --bg-primary-inverse:                    rgba(var(--bg-base-inverse), 1);
  --bg-elevated-primary-inverse:           rgba(var(--bg-base-inverse), 1);
  --bg-elevated-secondary-inverse:         rgba(var(--color-gray-1), 1);
  --bg-elevated-tertiary-inverse:          rgba(var(--bg-base-inverse), 1);
  --bg-secondary-inverse:                  rgba(var(--color-gray-1), 1);
  --bg-tertiary-inverse:                   rgba(var(--bg-base-inverse), .04);
  --bg-quarternary-inverse:                rgba(var(--bg-base-inverse), .02);

  --bg-disabled:            var(--bg-quarternary);
  --bg-disabled-inverse:    var(--bg-quarternary-inverse);
  --bg-0:                   rgba(var(--bg-base),0);
  --bg-70:                  rgba(var(--bg-base),.7);
  --bg-80:                  rgba(var(--bg-base),.8);
  --bg-70-inverse:          rgba(var(--bg-base-inverse),.7);
  --bg-80-inverse:          rgba(var(--bg-base-inverse),.8);
  
  /* Fill — base (solid) + elevated (translucent overlay) */
  --fill-primary:                     #333333;
  --fill-primary-inverse:             #CCCCCC;
  --fill-secondary:                   #1F1F1F;
  --fill-secondary-inverse:           #E0E0E0;
  --fill-tertiary:                    #121212;
  --fill-tertiary-inverse:            #EDEDED;
  --fill-elevated-primary:            rgba(120, 120, 128, 0.36);
  --fill-elevated-primary-inverse:    rgba(120, 120, 120, 0.20);
  --fill-elevated-secondary:          rgba(120, 120, 128, 0.32);
  --fill-elevated-secondary-inverse:  rgba(120, 120, 128, 0.16);
  --fill-elevated-tertiary:           rgba(118, 118, 128, 0.24);
  --fill-elevated-tertiary-inverse:   rgba(118, 118, 128, 0.12);
  --fill-elevated-quaternary:         rgba(118, 118, 128, 0.18);
  --fill-elevated-quaternary-inverse: rgba(116, 116, 128, 0.08);


  /* Text */
  --text-primary:             #FFFFFF;
  --text-primary-inverse:     #0D0D0D;
  --text-secondary:           rgba(255, 255, 255, 0.5);
  --text-secondary-inverse:   rgba(0, 0, 0, 0.5);
  --text-tertiary:            #AFAFAF;
  --text-tertiary-inverse:    rgba(0, 0, 0, 0.4);
  --text-quarternary:         #9B9B9B;
  --text-quarternary-inverse: rgba(0, 0, 0, 0.3);
  --text-disabled:            rgba(255, 255, 255, 0.2);
  --text-disabled-inverse:    rgba(0, 0, 0, 0.2);

  
  --text-primary:             #FFFFFF;
  --text-primary-inverse:     #000000;
  --text-secondary:           rgba(235,235,245,0.7);
  --text-secondary-inverse:   rgba(60,60,67,0.6);
  --text-tertiary:            rgba(235,235,245,0.3);
  --text-tertiary-inverse:    rgba(60,60,67,0.3);
  --text-quarternary:         rgba(235,235,245,0.16);
  --text-quarternary-inverse: rgba(60,60,67,0.18);
  --text-disabled:              var(--text-quarternary);
  --text-disabled-inverse:      var(--text-quarternary-inverse);

  --text-primary-vibrant:            #F5F5F5;
  --text-primary-vibrant-inverse:    #1A1A1A;
  --text-secondary-vibrant:          #8A8A8A;
  --text-secondary-vibrant-inverse:  #727272;
  --text-tertiary-vibrant:           #404040;
  --text-tertiary-vibrant-inverse:   #BFBFBF;
  --text-quarternary-vibrant:        #262626;
  --text-quarternary-vibrant-inverse:#D9D9D9;
  --text-disabled-vibrant:             var(--text-quarternary-inverse);
  --text-disabled-vibrant-inverse:     var(--text-quarternary-vibrant);


  /* Border */
  --border:           rgba(255, 255, 255, 0.3);
  --border-strong:    rgba(255, 255, 255, 0.5);
  --border-primary:   #38383A;
  --border-secondary: rgba(255, 255, 255, 0.17);
  --border-tertiary:  #1A1A1A;

  /* Accent */
  --accent-blue:                  rgba(var(--color-blue), 1);
  --accent-cobalt:                #3b60e4;
  --accent-red:                   rgba(var(--color-red), 1);
  --accent-green:                 rgba(var(--color-green), 1);
  --accent-yellow:                rgba(var(--color-yellow), 1);
  --accent-orange:                rgba(var(--color-orange), 1);
  --accent-purple:                rgba(var(--color-purple), 1);
  
  --accent-blue-inverse:          rgba(var(--color-blue-inverse), 1);
  --accent-red-inverse:           rgba(var(--color-red-inverse), 1);
  --accent-green-inverse:         rgba(var(--color-green-inverse), 1);
  --accent-yellow-inverse:        rgba(var(--color-yellow-inverse), 1);
  --accent-orange-inverse:        rgba(var(--color-orange-inverse), 1);
  --accent-purple-inverse:        rgba(var(--color-purple-inverse), 1);

  
  /* Timeline list (iOS parity) */
  --canvas-bg:         #1C1C1E;
  --tl-text-primary:   #FEFFFF;
  --tl-text-secondary: rgba(235, 235, 245, 0.7);
  --ui-borders:        #38383A;
  --brand-blue:        #0091FF;
  --brand-blue-12:     rgba(0, 145, 255, 0.12);

  /* Shadow */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow:     0 2px 8px rgba(0, 0, 0, 0.4);

  /* Deprecated aliases — remove once consumers are migrated */
  --bg:         #0a0a0a;
  --bg-subtle:  #171717;
  --bg-elevated: #1c1c1c;
  --fg:         #fafafa;
  --fg-muted:   #a3a3a3;
  --fg-subtle:  #737373;
  --accent:     #fafafa;
  --accent-fg:  #0a0a0a;
  --danger:     #f87171;

  color-scheme: dark;
}


/* ─── OS dark preference — catch-all when no data-theme is set ────────────── */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {

    --blend-mode: lighten;
      
    --bg-base:                var(--neutral-black);
    --bg-base-inverse:        var(--neutral-white);

    --color-gray-1:           var(--neutral-01);
    --color-gray-2:           var(--neutral-02);
    --color-gray-3:           var(--neutral-03);
    --color-gray-4:           var(--neutral-04);
    --color-gray-5:           var(--neutral-05);

    --color-red:              var(--red-2);
    --color-orange:           var(--orange-2);
    --color-yellow:           var(--yellow-2);
    --color-green:            var(--green-2);
    --color-blue:             var(--blue-2);
    --color-purple:           var(--purple-2);

    --color-red-inverse:      var(--red-1);
    --color-orange-inverse:   var(--orange-1);
    --color-yellow-inverse:   var(--yellow-1);
    --color-green-inverse:    var(--green-1);
    --color-blue-inverse:     var(--blue-1);
    --color-purple-inverse:   var(--purple-1);

    /* Background */
    --bg-body:               rgba(var(--color-gray-5), 1);
    --bg-primary:            rgba(var(--bg-base), 1); 
    --bg-secondary:          rgba(var(--color-gray-4), 1);
    --bg-tertiary:           rgba(var(--color-gray-3), 1);
    --bg-quarternary:        rgba(var(--bg-base), .02);

    --bg-elevated-primary:   rgba(var(--color-gray-4), 1);
    --bg-elevated-secondary: rgba(var(--color-gray-3), 1);
    --bg-elevated-tertiary:  rgba(var(--color-gray-2), 1);   

    --bg-body-inverse:                       rgba(var(--bg-base-inverse), 1);
    --bg-primary-inverse:                    rgba(var(--bg-base-inverse), 1);
    --bg-elevated-primary-inverse:           rgba(var(--bg-base-inverse), 1);
    --bg-elevated-secondary-inverse:         rgba(var(--color-gray-1), 1);
    --bg-elevated-tertiary-inverse:          rgba(var(--bg-base-inverse), 1);
    --bg-secondary-inverse:                  rgba(var(--color-gray-1), 1);
    --bg-tertiary-inverse:                   rgba(var(--bg-base-inverse), .04);
    --bg-quarternary-inverse:                rgba(var(--bg-base-inverse), .02);

    --bg-disabled:            var(--bg-quarternary);
    --bg-disabled-inverse:    var(--bg-quarternary-inverse);
    --bg-0:                   rgba(var(--bg-base),0);
    --bg-70:                  rgba(var(--bg-base),.7);
    --bg-80:                  rgba(var(--bg-base),.8);
    --bg-70-inverse:          rgba(var(--bg-base-inverse),.7);
    --bg-80-inverse:          rgba(var(--bg-base-inverse),.8);

    /* Fill — base (solid) + elevated (translucent overlay) */
    --fill-primary:                     #333333;
    --fill-primary-inverse:             #CCCCCC;
    --fill-secondary:                   #1F1F1F;
    --fill-secondary-inverse:           #E0E0E0;
    --fill-tertiary:                    #121212;
    --fill-tertiary-inverse:            #EDEDED;
    --fill-elevated-primary:            rgba(120, 120, 128, 0.36);
    --fill-elevated-primary-inverse:    rgba(120, 120, 120, 0.20);
    --fill-elevated-secondary:          rgba(120, 120, 128, 0.32);
    --fill-elevated-secondary-inverse:  rgba(120, 120, 128, 0.16);
    --fill-elevated-tertiary:           rgba(118, 118, 128, 0.24);
    --fill-elevated-tertiary-inverse:   rgba(118, 118, 128, 0.12);
    --fill-elevated-quaternary:         rgba(118, 118, 128, 0.18);
    --fill-elevated-quaternary-inverse: rgba(116, 116, 128, 0.08);

    /* Text */
    --text-primary:             #FFFFFF;
    --text-primary-inverse:     #0D0D0D;
    --text-secondary:           rgba(255, 255, 255, 0.5);
    --text-secondary-inverse:   rgba(0, 0, 0, 0.5);
    --text-tertiary:            #AFAFAF;
    --text-tertiary-inverse:    rgba(0, 0, 0, 0.4);
    --text-quarternary:         #9B9B9B;
    --text-quarternary-inverse: rgba(0, 0, 0, 0.3);
    --text-disabled:            rgba(255, 255, 255, 0.2);
    --text-disabled-inverse:    rgba(0, 0, 0, 0.2);

    
    --text-primary:             #FFFFFF;
    --text-primary-inverse:     #000000;
    --text-secondary:           rgba(235,235,245,0.7);
    --text-secondary-inverse:   rgba(60,60,67,0.6);
    --text-tertiary:            rgba(235,235,245,0.3);
    --text-tertiary-inverse:    rgba(60,60,67,0.3);
    --text-quarternary:         rgba(235,235,245,0.16);
    --text-quarternary-inverse: rgba(60,60,67,0.18);
    --text-disabled:              var(--text-quarternary);
    --text-disabled-inverse:      var(--text-quarternary-inverse);

    --text-primary-vibrant:            #F5F5F5;
    --text-primary-vibrant-inverse:    #1A1A1A;
    --text-secondary-vibrant:          #8A8A8A;
    --text-secondary-vibrant-inverse:  #727272;
    --text-tertiary-vibrant:           #404040;
    --text-tertiary-vibrant-inverse:   #BFBFBF;
    --text-quarternary-vibrant:        #262626;
    --text-quarternary-vibrant-inverse:#D9D9D9;
    --text-disabled-vibrant:             var(--text-quarternary-inverse);
    --text-disabled-vibrant-inverse:     var(--text-quarternary-vibrant);

    /* Border */
    --border:           rgba(255, 255, 255, 0.3);
    --border-strong:    rgba(255, 255, 255, 0.5);
    --border-primary:   #38383A;
    --border-secondary: rgba(255, 255, 255, 0.17);
    --border-tertiary:  #1A1A1A;

    /* Accent */
    --accent-blue:                  rgba(var(--color-blue), 1);
    --accent-cobalt:                #3b60e4;
    --accent-red:                   rgba(var(--color-red), 1);
    --accent-green:                 rgba(var(--color-green), 1);
    --accent-yellow:                rgba(var(--color-yellow), 1);
    --accent-orange:                rgba(var(--color-orange), 1);
    --accent-purple:                rgba(var(--color-purple), 1);
    
    --accent-blue-inverse:          rgba(var(--color-blue-inverse), 1);
    --accent-red-inverse:           rgba(var(--color-red-inverse), 1);
    --accent-green-inverse:         rgba(var(--color-green-inverse), 1);
    --accent-yellow-inverse:        rgba(var(--color-yellow-inverse), 1);
    --accent-orange-inverse:        rgba(var(--color-orange-inverse), 1);
    --accent-purple-inverse:        rgba(var(--color-purple-inverse), 1);

    /* Timeline list (iOS parity) */
    --canvas-bg:         #1C1C1E;
    --tl-text-primary:   #FEFFFF;
    --tl-text-secondary: rgba(235, 235, 245, 0.7);
    --ui-borders:        #38383A;
    --brand-blue:        #0091FF;
    --brand-blue-12:     rgba(0, 145, 255, 0.12);

    /* Shadow */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow:     0 2px 8px rgba(0, 0, 0, 0.4);

    /* Deprecated aliases — remove once consumers are migrated */
    --bg:         #0a0a0a;
    --bg-subtle:  #171717;
    --bg-elevated: #1c1c1c;
    --fg:         #fafafa;
    --fg-muted:   #a3a3a3;
    --fg-subtle:  #737373;
    --accent:     #fafafa;
    --accent-fg:  #0a0a0a;
    --danger:     #f87171;

    color-scheme: dark;
  }
}

/* Reset + base element styles. Consumes tokens.css. */

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

html, body {
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  overscroll-behavior: none;
  /* Ensure the body never exceeds the true visual viewport on Android */
  max-height: 100dvh;
}

a { color: inherit; text-decoration: none; }
a:hover { text-decoration: underline; }

ul, ol { list-style: none; }

button {
  font: inherit;
  color: inherit;
  background: transparent;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  padding: var(--sp-xs) var(--sp-sm);
  cursor: pointer;
}
button:hover {
  border-color: var(--border-primary);
}

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

/* Prevent iOS Safari autozoom on field focus. The browser zooms whenever a
   focused field's font-size is < 16px, so floor every text-entry surface at
   16px on mobile — native controls (input / textarea / select) AND the
   contenteditable editors (Milkdown / ProseMirror), which the bare element
   selectors miss. `!important` is required because component classes set
   sub-16px sizes that win on specificity; `max(16px, 1em)` only ever raises,
   never shrinks. The `:not()`s preserve the few intentionally-large fields
   (phone entry, reminder title) and the off-screen file-input sizing hack.
   This intentionally does not disable pinch-zoom. */
@media (max-width: 768px) {
  input:not(.auth-phone-input):not(.capture-file-input-hidden),
  textarea:not(.nr-title),
  select,
  [contenteditable="true"],
  .ProseMirror {
    font-size: max(16px, 1em) !important;
  }

  /* Placeholder text. Real ::placeholder inherits the (now ≥16px) field size;
     the editors render their placeholder as a ::before pseudo-element that
     carries its own font-size, so floor those too. */
  .chat-composer-milkdown::before,
  .capture-text-editor .ProseMirror p.is-editor-empty::before {
    font-size: max(16px, 1em) !important;
  }
}

/* Lucide-style icons rendered via CSS mask so they inherit currentColor.
   Each icon span carries a `--icon-mask` custom property pointing to an
   inline SVG data URI, and explicit width/height. See `ui/icons.gleam`. */
.icon {
  display: inline-block;
  background-color: currentColor;
  -webkit-mask-image: var(--icon-mask);
          mask-image: var(--icon-mask);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  flex-shrink: 0;
  vertical-align: middle;
  line-height: 0;
  /* WebKit hit-tests against the mask alpha channel, so only painted pixels
     would register clicks. Opt out entirely — let the parent button/anchor
     be the sole click target. */
  pointer-events: none;
}

/* Brand-gradient tint through the icon mask (the mask only reads alpha,
   so a background gradient shows through the glyph). Generic sibling of
   sidebar.css's .sidebar-row-icon-gradient-green — wrap any icon:
     html.span([class("icon-gradient-green")], [icons.x(size)]) */
.icon-gradient-green .icon {
  background: var(--fill-gradient-green);
}

/* ── <waveform-tile> — 1:1 audio waveform preview ──────────────────────────
   Canvas bars draw in `currentColor`; size by setting a width on the
   element (or letting a square slot like the Activity media box or
   .tl-row-thumb size it). */
.wf-tile {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--fill-elevated-quaternary);
  border-radius: var(--radius-md);
  color: var(--text-tertiary);
  overflow: hidden;
  pointer-events: none; /* parent anchor owns the click, like .icon */
}

/* Animated inline-SVG icons (e.g. check_hollow_animated).
   Cannot use the mask approach — browsers don't animate SVG data URIs.
   Instead the SVG is injected as real DOM via unsafe_raw_html and wrapped
   in this span so it flows like a regular .icon. */
.icon-animated-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  vertical-align: middle;
  line-height: 0;
  pointer-events: none;
}

.icon-animated-check svg {
  display: block;
}

.pill {
    border: none;
    font: inherit;
    cursor: pointer;
}

.due-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: var(--sp-4xs) var(--sp-xs);
    border-radius: var(--radius-full);
    background: rgba(var(--color-blue), .1);
    color: var(--accent-blue);
    background-blend-mode: lighten;
    height: 24px;
}

.due-pill > .due-pill-label {
    font-size: var(--text-base);
    font-weight: 400;
    line-height: 18px;
    letter-spacing: -0.08px;
}

/* Empty state ("Set alert"): same geometry, neutral/grey capsule. Carried
   over from feed.css's old .due-pill--empty. */
.due-pill--empty {
    background: var(--fill-elevated-quaternary);
    color: var(--text-primary);
}
/* ── Tooltip — data-tooltip attribute ───────────────────────────────
   Usage: add  data-tooltip="Your label"  to any element.
   The tooltip appears above the element, centred, on hover/focus.
   ------------------------------------------------------------------ */

[data-tooltip] {
  position: relative;
}

[data-tooltip]::before,
[data-tooltip]::after {
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease;
  z-index: 200;
}

/* Bubble */
[data-tooltip]::before {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%);
  padding: 4px 9px;
  background: var(--bg-elevated, var(--bg-primary));
  color: var(--text-primary);
  font-size: var(--text-xs);
  font-weight: 500;
  font-family: inherit;
  white-space: nowrap;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-primary);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

/* Arrow caret (downward-pointing triangle below the bubble) */
[data-tooltip]::after {
  content: "";
  position: absolute;
  bottom: calc(100% + 3px);
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top: 6px solid var(--border-primary);
}

[data-tooltip]:hover::before,
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::before,
[data-tooltip]:focus-visible::after {
  opacity: 1;
}

/* Suppress tooltip while the element is active/pressed */
[data-tooltip]:active::before,
[data-tooltip]:active::after {
  opacity: 0;
}

.button {
    width: 100%;
    padding: var(--sp-xs) var(--sp-md);
    background: transparent;
    /* Neutralize the UA <button> border; variants (.danger) re-add their
       own. */
    border: none;
    border-radius: var(--radius-full);
    font-family: inherit;
    font-size: var(--text-base);
    font-weight: 500;
    cursor: pointer;
}

.button.destructive {
    color: var(--accent-red);
    background: rgba(var(--color-red), .1);
    background-blend-mode: var(--blend-mode);
}

.button.danger {
    color: var(--accent-red);
    background: transparent;
    border: 1px solid var(--accent-red);
}
/* Phone + OTP authentication screen — centered card on a muted background. */

.auth-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-subtle);
  padding: var(--sp-xl);
}

.auth-card {
  width: 100%;
  max-width: 440px;
  padding: var(--sp-xxl);
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.auth-logo {
  font-family: var(--font-serif);
  font-size: var(--text-3xl);
  font-style: italic;
  font-weight: 400;
  text-align: center;
  margin: 0 0 var(--sp-xs);
  color: var(--text-primary);
}

.auth-title {
  font-size: var(--text-lg);
  font-weight: 500;
  text-align: center;
  color: var(--text-tertiary);
  margin: 0 0 var(--sp-xl);
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

.auth-field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.auth-label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.auth-field input,
.auth-field-input {
  padding: var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font: inherit;
  outline: none;
  transition: border-color 0.15s ease;
  width: 100%;
  box-sizing: border-box;
}

.auth-field input:focus,
.auth-field-input:focus {
  border-color: var(--bg-secondary);
}

.auth-error {
  font-size: var(--text-sm);
  color: var(--accent-red);
  text-align: center;
  margin: 0;
}

.auth-submit {
  padding: var(--sp-sm);
  border: none;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  margin-top: var(--sp-xs);
  transition: background 0.15s ease, color 0.15s ease;
}

.auth-submit:not(:disabled) {
  background: var(--fg);
  color: var(--bg);
}

.auth-submit:disabled {
  cursor: not-allowed;
}

/* Phone input */
.auth-phone-input {
  font-size: var(--text-2xl);
  text-align: center;
  letter-spacing: 0.1em;
  padding: var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-sans);
  width: 100%;
  box-sizing: border-box;
}
.auth-phone-input:focus { border-color: var(--bg-secondary); outline: none; }

.auth-phone-row {
  display: flex;
  gap: 8px;
  width: 100%;
}

.auth-country-button {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  cursor: pointer;
  font: inherit;
  white-space: nowrap;
}

.auth-country-button:hover {
  background: var(--bg-subtle);
}

.auth-country-flag {
  font-size: 18px;
  line-height: 1;
}

.auth-country-code {
  font-weight: 500;
}

.auth-country-caret {
  font-size: 10px;
  opacity: 0.6;
}

.auth-phone-row .auth-phone-input {
  flex: 1;
  min-width: 0;
}

.auth-country-picker {
  position: relative;
  margin-top: 8px;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  max-height: 300px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  z-index: 10;
}

.auth-country-search {
  padding: 10px 12px;
  border: none;
  border-bottom: 1px solid var(--border-primary);
  font: inherit;
  outline: none;
  background: transparent;
}

.auth-country-list {
  overflow-y: auto;
  flex: 1;
}

.auth-country-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  border: none;
  background: transparent;
  cursor: pointer;
  font: inherit;
  text-align: left;
}

.auth-country-row:hover {
  background: var(--bg-subtle);
}

.auth-country-row-flag {
  font-size: 18px;
  line-height: 1;
}

.auth-country-row-name {
  flex: 1;
}

.auth-country-row-code {
  opacity: 0.6;
}

/* OTP display */
.auth-otp-wrapper { position: relative; }

.auth-otp-hidden-input {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 100%;
  height: 100%;
  font-size: 24px;
}

.auth-otp-display {
  display: flex;
  gap: var(--sp-sm);
  justify-content: center;
}

.auth-otp-box {
  width: 48px;
  height: 56px;
  border: 2px solid var(--border-primary);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-2xl);
  font-weight: 500;
  color: var(--text-primary);
  background: var(--bg);
}
.auth-otp-box-active { border-color: var(--bg-secondary); }

/* OTP sub-text */
.auth-phone-hint {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  text-align: center;
  margin-top: var(--sp-xs);
}

.auth-resend {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  text-align: center;
  margin-top: var(--sp-sm);
}
.auth-resend-btn {
  background: none;
  border: none;
  color: var(--bg-secondary);
  cursor: pointer;
  font: inherit;
  padding: 0;
}
.auth-resend-btn:hover { text-decoration: underline; }

/* Interest picker */
.auth-interest-form {
  gap: var(--sp-md);
}

.auth-subtitle {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  text-align: center;
  margin: 0 0 var(--sp-xs);
}

.auth-interest-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-xs);
  justify-content: center;
  min-height: 80px;
}

.auth-interest-loading {
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: var(--sp-xl) 0;
}

.auth-interest-chip {
  padding: 6px 14px;
  border-radius: 20px;
  border: 1px solid var(--border-primary);
  background: transparent;
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.auth-interest-chip:hover {
  background: var(--bg-subtle);
}

.auth-interest-chip.selected {
  background: var(--fg);
  color: var(--bg);
  border-color: var(--fg);
}

.auth-interest-hint {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  text-align: center;
  margin: 0;
}

.auth-interest-buttons {
  display: flex;
  gap: var(--sp-sm);
}

.auth-interest-buttons .auth-submit {
  flex: 1;
  margin-top: 0;
}

.auth-submit-secondary {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
}

.auth-submit-secondary:hover:not(:disabled) {
  background: var(--bg-subtle);
}

/* Back link */
.auth-back {
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  margin-bottom: var(--sp-md);
}
.auth-back:hover { color: var(--text-primary); }

/* Shell layout styles — app-shell root, shared page-header primitives,
 * main content pane, and the mobile bottom action bar.
 * Sidebar navigation lives in sidebar.css (imported right after this file).
 */

.app-shell {
  display: flex;
  height: 100vh;                  /* fallback: no dvh support */
  height: 100dvh;                 /* fallback: dvh but no visualViewport JS */
  height: var(--vvh, 100dvh);    /* primary: set in real-time by dom.mjs from
                                     visualViewport.height — reliably shrinks when
                                     the iOS Safari keyboard appears */
  overflow: hidden;
  /* Respect the gesture-nav bar on Android and iOS notch devices */
  padding-bottom: env(safe-area-inset-bottom);
}

/* Keyboard up: the on-screen keyboard covers the gesture-nav zone, but
   env() keeps reporting the inset — collapse it so bottom-anchored UI
   hugs the keyboard instead of floating above it (.kb-open is set by
   the --vvh visualViewport updater in dom.mjs). */
.kb-open .app-shell {
  padding-bottom: 0;
}

/* ── Shared header icon button ──────────────────────────────────────────
   Ghost, fully-round icon button used for every header action across
   Activity, Messages, Please AI chat, Reminders/Timeline, Profile, and
   Settings — sidebar toggle, back/close, search, compose, menu, settings.
   Desktop: 32x32 hit box / 20px icon. Mobile: 48x48 hit box / 32px icon.
   Icon calls should pass size 0 so this class controls the icon size. */

/* ── Standardized page header (ui/page_header) ─────────────────────────
   Extracted from .chat-header (chat.css) — values mirrored exactly.
   [leading action] [centered flex:1 title] [trailing actions]; the
   leading slot swaps back-link (desktop) ↔ sidebar-toggle (≤768px). */
.page-header {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  /* Top: sp-sm + sp-xs = 20px — the same two-token stack as the sidebar
     (.sidebar 12px + .sidebar-header 8px), so the page header and the
     sidebar header top-align exactly. Page roots must not add their own
     top padding above this. */
  padding: calc(var(--sp-sm) + var(--sp-xs)) var(--sp-md) 0;
  flex-shrink: 0;
  min-height: 68px;
}

/* Action slots: equal flex share on both sides keeps the title truly
   centered regardless of how many actions each side holds; an empty slot
   still occupies its share. min-width reserves at least one icon-button
   footprint. */
.page-header-slot {
  flex: 1 1 0;
  min-width: 32px;
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
}
.page-header-slot--left {
  justify-content: flex-start;
}
.page-header-slot--right {
  justify-content: flex-end;
}

.page-header-title {
  flex: 0 1 auto;
  min-width: 0;
  text-align: center;
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.page-header-title-link {
  text-decoration: none;
  cursor: pointer;
}
.page-header-title-link:hover {
  text-decoration: none;
  color: var(--accent-blue);
}

/* Viewport pinning for contextual actions: views keep whatever actions
   they carry today per width by tagging them with one of these.
   Scoped under .page-header — an unscoped single class ties with
   .header-icon-btn's `display: inline-flex` on specificity and loses on
   source order, which made "hidden" actions render anyway. */
.page-header .page-header-sidebar-toggle,
.page-header .page-header-mobile-only {
  display: none;
}

@media (max-width: 768px) {
  .page-header .page-header-desktop-only {
    display: none;
  }
  .page-header .page-header-sidebar-toggle,
  .page-header .page-header-mobile-only {
    display: inline-flex;
  }

  .page-header-title {
    font-size: var(--text-2xl);
  }
}

.header-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-primary);
  text-decoration: none;
  cursor: pointer;
  /* Fade the ghost background in on hover/focus and back out on leave,
     instead of snapping instantly. */
  transition: background-color 150ms ease, color 150ms ease;
}

.header-icon-btn:hover,
.header-icon-btn:focus-visible {
  background: var(--bg-subtle);
  color: var(--text-primary);
  text-decoration: none;
}

/* Quick fade-down on press for tactile feedback. */
.header-icon-btn:active {
  background: var(--bg-subtle);
  transition-duration: 60ms;
}

.header-icon-btn .icon {
  width: 20px;
  height: 20px;
}

@media (max-width: 768px) {
  .header-icon-btn {
    width: 48px;
    height: 48px;
  }

  .header-icon-btn .icon {
    width: 24px;
    height: 24px;
  }
}

/* Main content pane beside the sidebar */
.main-pane {
  flex: 1;
  min-width: 0;
  min-height: 0;
  height: 100%;
  overflow: hidden;
}

/* ── Mobile action bar (search + create) ─────────────────────────────────
   Search-pill + create-button styling for the persistent bottom bar shown
   on the Timeline view only (mobile). Desktop keeps Search/New Reminder in
   the sidebar, so this stays hidden above the mobile breakpoint.

   Fixed positioning over the scrolling item list lives in feed.css
   (`.feed .mobile-action-bar`), not here. */

.mobile-action-bar {
  display: none;
}

@media (max-width: 768px) {
  .mobile-action-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-sm);
    padding: var(--sp-lg) var(--sp-md) calc(var(--sp-xl) + env(safe-area-inset-bottom));
  }

  .mobile-action-bar-search {
    flex: 1;
    min-width: 0;
    max-width: 350px;
    display: flex;
    align-items: center;
    gap: var(--sp-xs);
    height: 48px;
    padding: 0 var(--sp-md);
    border-radius: var(--radius-full);
    border: 1px solid var(--border-secondary);
    background: var(--bg-70);
    color: var(--text-tertiary);
    backdrop-filter: blur(4px);
    text-decoration: none;
  }
  @media (hover: hover) {
    .mobile-action-bar-search:hover {
      text-decoration: none;
    }
  }

  .mobile-action-bar-search-label {
    font-size: var(--text-lg);
    color: var(--text-tertiary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .mobile-action-bar-create {
    flex-shrink: 0;
    width: 48px;
    height: 48px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full);
    border: none;
    background: var(--fill-gradient-green);
    color: #ffffff;
    cursor: pointer;
    padding: 0;
  }
  @media (hover: hover) {
    .mobile-action-bar-create:hover {
      filter: brightness(1.05);
    }
  }
}

@media (max-width: 768px) {
  .mobile-action-bar-search {
    height: var(--size-5xl);
  }
  .mobile-action-bar-create {
    height: var(--size-5xl);
    width: var(--size-5xl);
  }
}  

/* Sidebar navigation — desktop persistent rail / mobile slide-in drawer.
 * Extracted from shell.css for granularity (shell.css now holds only the
 * app-shell root, the shared page-header/header-icon-btn primitives, the
 * main content pane, and the mobile bottom action bar).
 *
 * Responsive breakpoints:
 *   Mobile  : max-width 768px  — sidebar hidden, slide-in drawer
 *   Desktop : min-width 769px  — sidebar always visible (240px or 64px collapsed)
 */

/* ── Mobile nav backdrop ──────────────────────────────────────────────── */

.mobile-nav-backdrop {
  display: none;
}

.sidebar {
  width: 240px;
  flex-shrink: 0;
  background: var(--bg-primary);
  border-right: 1px solid var(--border-tertiary);
  display: flex;
  flex-direction: column;
  /* 10px horizontal padding aligns icons between expanded and collapsed:
     10px (sidebar) + 12px (row) = 22px icon offset, matching
     the auto-margin centering of the 44px square pill in collapsed state */
  padding: var(--sp-sm) 10px;
  gap: var(--sp-md);
  box-sizing: border-box;
  position: sticky;
  top: 0;
  height: 100vh;   /* fallback */
  height: 100dvh;  /* dynamic viewport — keeps sidebar flush with the shell */
  overflow-x: hidden;
  overflow-y: hidden; /* .sidebar-scroll owns scrolling; header/footer stay pinned */
  transition: width 0.3s ease-in-out, padding 0.3s ease-in-out;
}

.sidebar-collapsed {
  width: 72px;
  /* 10px horizontal padding: sidebar(10) + row(12) = 22px icon offset,
     matching expanded state. Row width = 72 - 10 - 10 = 52px. */
  padding-left: 10px;
  padding-right: 10px;
}

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-xs) var(--sp-xs) var(--sp-xs);
}

.sidebar-logo {
  font-size: var(--text-2xl);
  color: var(--text-primary);
  line-height: 1;
  text-decoration: none;
  max-width: 200px;
  overflow: hidden;
  white-space: nowrap;
  opacity: 1;
  flex: 1;
  transition: max-width 0.3s ease-in-out, opacity 0.3s ease-in-out;
}
@media (hover: hover) {
  .sidebar-logo:hover {
    text-decoration: none;
  }
}

.sidebar-logo > .icon {
  max-width: 70px;
  transition: max-width 0.3s ease-in-out, opacity 0.3s ease-in-out;
}

.sidebar-collapse-button {
  /* Sizing, color, ghost background, and radius now come from
     .header-icon-btn (shared header button styles above) — this rule only
     keeps what's specific to the sidebar's own toggle. */
  border: none;
  cursor: pointer;
  /* position: relative so the ::after overlay is anchored to the button */
  position: relative;
}

.sidebar-collapse {
  padding: var(--sp-3xs);
  border: none;
  color: var(--text-secondary);
  font-size: var(--text-lg);
  cursor: pointer;
  border-radius: var(--radius-sm);
  position: relative;
}

@media (hover: hover) {
  .sidebar-collapse-button:hover,
  .sidebar-collapse:hover {
    color: var(--text-primary);
    transition: .3s ease all;
  }
}

/* Chrome falls through `background: transparent` buttons in hollow-icon areas
   because there's no painted surface to hit-test against. This ::after creates
   a full-coverage invisible layer that IS painted, forcing the button to catch
   every click within its bounds. */
.sidebar-collapse-button::after,
.sidebar-collapse::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: rgba(0, 0, 0, 0.001); /* near-zero alpha: painted surface guarantees hit-testing in Chrome */
}

.sidebar-collapse>.icon {
  height: 20px !important;
  width: 20px !important;
}

.sidebar-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sidebar-section-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--sp-sm) var(--sp-xs) var(--sp-xs) var(--sp-xs);
  overflow: hidden;
  white-space: nowrap;
  opacity: 1;
  transition: opacity 0.3s ease-in-out;
  /* Pins to the top of .sidebar-scroll once scrolled up to it, so chat rows
     scroll underneath instead of the header scrolling out of view. Opaque
     background (same token the sidebar itself uses) covers rows passing
     behind it; unstuck, it's visually identical to the old static header. */
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-primary);
}

.sidebar-section-header__label {
  font-size: var(--text-lg); /* 16px */
  color: var(--text-primary);
}

/* Collapsed-rail label swap: "Messages" ↔ "Msg". The short label hides by
   default; the desktop collapsed state flips both (and drops the + action,
   which has nowhere sane to live in the 72px rail). The mobile drawer
   never collapses visually, so the mobile block below restores the full
   set like the other collapsed resets. */
.sidebar-section-header__label--collapsed {
  display: none;
}

.sidebar-collapsed .sidebar-section-header__label--full,
.sidebar-collapsed .sidebar-section-header-action {
  display: none;
}

.sidebar-collapsed .sidebar-section-header__label--collapsed {
  display: inline;
}

/* "+" action in a section header (e.g. Messages → new chat). */
.sidebar-section-header-action {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
}

@media (hover: hover) {
  .sidebar-section-header-action:hover {
    background: var(--fill-elevated-secondary);
  }
}

.sidebar-row {
  /* Button reset — ensures <button> rows are identical to <a> rows */
  font: inherit;
  border: none;
  background: none;
  box-sizing: border-box;
  /* Row layout */
  display: flex;
  align-items: center;
  gap: var(--sp-xxs);
  padding: 6px var(--sp-sm);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  color: var(--text-primary);
  min-height: 44px;
  text-decoration: none !important;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  /* Smooth the padding change between expanded (6px 12px) and collapsed (12px) */
  transition: padding 0.3s ease-in-out;
  position: relative;
  flex-shrink: 0;
}

@media (hover: hover) {
  .sidebar-row:hover {
  background: var(--fill-elevated-secondary);
  text-decoration: none;
  transition: .3s ease-in-out;
}
}
.sidebar-row-active {
  background: var(--fill-elevated-secondary);
  font-weight: 500;
  box-shadow: none;
}
@media (hover: hover) {
  .sidebar-row-active:hover {
  background: var(--fill-elevated-secondary);
  font-weight: 500;
  box-shadow: none;
}
}

.sidebar-row-icon {
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: var(--text-primary);
  font-size: var(--text-base);
}

/* Brand-gradient icon tint (e.g. New Reminder). The base .icon paints
   currentColor through an alpha mask, so a background-image gradient
   shows through the same mask. */
.sidebar-row-icon-gradient-green .icon {
  background: var(--fill-gradient-green);
}

.sidebar-row-active .sidebar-row-icon {
  color: var(--text-primary);
}

.sidebar-label {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 21px;
  letter-spacing: -0.31px;
  max-width: 200px;
  opacity: 1;
  transition: max-width 0.3s ease-in-out, opacity 0.3s ease-in-out;
}

.sidebar-row-active .sidebar-label,
.sidebar-row-button .sidebar-label {
  color: var(--text-primary);
}

.sidebar-label--with-you {
  display: flex;
  align-items: center;
  gap: var(--sp-4xs);
}

.sidebar-label-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  min-width: 0;
}

.sidebar-label-you {
  color: var(--text-secondary);
  font-weight: 400;
  flex-shrink: 0;
}

.sidebar-row-unread .sidebar-label {
  font-weight: 600;
  color: var(--text-primary);
}

/* Typing indicator: animated gradient sweep on the label */
.sidebar-row-typing .sidebar-label {
  font-weight: 600;
  background: radial-gradient(
    circle at 100%,
    #fda8a8,
    #f97878 50%,
    #fec7c7 75%,
    #fd8d8d 75%
  );
  background-size: 200% auto;
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: sidebar-typing-gradient 1.5s linear infinite;
}

.sidebar-unread-badge {
  display: none;
}

/* Single scroll region for nav rows + Messages (wraps view_nav + view_chats
   in shell.gleam). Fills the remaining space between header and footer, so
   those two stay pinned exactly as before — only this container scrolls.
   `gap` matches .sidebar's own gap so the nav/Messages sections keep the
   same spacing they had as direct children of .sidebar. The Messages
   section header (.sidebar-section-header) sticks to the top of this
   container once scrolled up to it; everything else scrolls normally. */
.sidebar-scroll {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sidebar-scroll::-webkit-scrollbar {
  display: none;
}

/* Spacer no longer needed — .sidebar-scroll grows to fill the gap */
.sidebar-spacer {
  display: none;
}

.sidebar-footer {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm);
  font-size: var(--text-sm);
  color: var(--text-primary);
  background: var(--bg-primary);
  flex-shrink: 0;
  transition: padding 0.3s ease-in-out, gap 0.3s ease-in-out;
}

/* Collapsed variant — animate text out, keep icons/avatars.
   Section headers (e.g. "Chats") stay visible even when collapsed. */
.sidebar-collapsed .sidebar-label,
.sidebar-collapsed .sidebar-logo {
  gap: 0;
  max-width: 0;
  opacity: 0;
}

/* Collapsed rows: uniform 12px padding makes each row a 44px square.
   Width comes from sidebar (64 - 10 - 10 = 44px) — no explicit width needed,
   so rows follow the sidebar width animation naturally. */
.sidebar-collapsed .sidebar-row {
  padding: 12px;
  justify-content: center;
  gap: 0;
}

.sidebar-collapsed > .sidebar-logo > .icon {
  max-width: 0;
}

/* Center the toggle button in the 72px collapsed sidebar.
   justify-content can't be transitioned, but the width animation masks the jump. */
.sidebar-collapsed .sidebar-header {
  justify-content: center;
}

/* Footer: strip padding/gap and center avatar directly in the 72px sidebar */
.sidebar-collapsed .sidebar-footer {
  flex-direction: column;
  padding: 0;
  gap: 0;
  align-items: center;
}

/* Fade the theme toggle out alongside the labels */
.sidebar-collapsed .sidebar-theme-toggle {
  display: none;
  max-width: 0;
  opacity: 0;
}

/* 36px avatar button, centered by the parent's align-items: center */
.sidebar-collapsed .sidebar-footer-user {
  height: 36px;
  width: 36px;
  flex-direction: column;
  padding: var(--sp-xs);
}

.sidebar-ai-avatar {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  background: var(--fg);
  color: var(--bg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.sidebar-footer-menu-anchor {
  position: relative;
  flex: 1;
  min-width: 0;
}

.sidebar-footer-user {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  color: inherit;
  background: none;
  border: none;
  font: inherit;
  cursor: pointer;
  padding: 0;
  width: 100%;
  overflow: hidden;
  transition: padding 0.3s ease-in-out;
}
@media (hover: hover) {
  .sidebar-footer-user:hover {
  text-decoration: none;
}
}

.user-menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 99;
  cursor: default;
}

.user-menu {
  position: absolute;
  bottom: calc(100% + var(--sp-xs));
  left: 0;
  min-width: 180px;
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  padding: var(--sp-3xs);
  z-index: 100;
}

/* When collapsed the sidebar's overflow-x: hidden clips the absolute menu.
   Switch to fixed so it escapes the clipping context and pops out to the
   right of the sidebar instead of trying to render inside it. */
.sidebar-collapsed .user-menu {
  position: fixed;
  bottom: var(--sp-md);
  left: calc(72px + var(--sp-xs));
}

.user-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  width: 100%;
  padding: var(--sp-xs) var(--sp-sm);
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  text-decoration: none;
  cursor: pointer;
}
@media (hover: hover) {
  .user-menu-item:hover {
  background: var(--bg-subtle);
  text-decoration: none;
}
}

@media (hover: hover) {
  .user-menu-danger:hover {
  color: var(--accent-red);
}
}

.user-menu-divider {
  height: 1px;
  background: var(--border-primary);
  margin: var(--sp-3xs) 0;
}

/* (Feed column toggle removed — internal test UI; the feed still reads
   the persisted column count via feed_cols_ffi.) */

.sidebar-theme-toggle {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  overflow: hidden;
  max-width: 28px;
  opacity: 1;
  transition: max-width 0.3s ease-in-out, opacity 0.3s ease-in-out;
}
@media (hover: hover) {
  .sidebar-theme-toggle:hover {
  color: var(--text-primary);
  background: var(--bg-primary);
}
}

/* ── Mobile responsive (≤ 768px) ─────────────────────────────────────── */

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes sidebar-typing-gradient {
  to {
    background-position: -200% center;
  }
}

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .sidebar-row-typing .sidebar-label {
    animation: none;
  }
}

@media (max-width: 768px) {
  /* Sidebar becomes an off-screen left drawer */
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 200;
    width: 280px;
    /* Always show expanded labels on mobile, ignore collapsed state */
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    box-shadow: 4px 0 32px rgba(0, 0, 0, 0.18);
  }

  /* Override the collapsed width so the open drawer always shows full labels */
  .sidebar-collapsed {
    width: 280px;
    padding-left: var(--sp-md);
    padding-right: var(--sp-md);
  }

  .sidebar-row {
    padding: var(--sp-sm) var(--sp-md);
  }

  .sidebar-label {
    font-size: var(--text-lg);
  }

  /* Reveal the drawer */
  .app-shell.nav-open .sidebar {
    transform: translateX(0);
  }

  /* Backdrop dims the content behind the open drawer */
  .mobile-nav-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: 199;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
  }

  .app-shell.nav-open .mobile-nav-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  /* Sidebar collapse button: always visible on mobile as a "close drawer" button */
  .sidebar-collapse-button {
    display: flex;
  }

  /* ── Reset ALL collapsed-sidebar visual effects inside the mobile drawer ──
     The desktop `.sidebar-collapsed` rules hide labels and compact rows for
     the 72px icon strip. On mobile the drawer is always full-width with labels,
     so every collapsed effect must be explicitly undone here. */

  /* Labels and logo: restore visibility */
  .sidebar-collapsed .sidebar-label,
  .sidebar-collapsed .sidebar-logo {
    max-width: 200px;
    opacity: 1;
  }

  /* Section header: restore visibility (base rule is full-width now —
     no max-width to undo, just the opacity fade) */
  .sidebar-collapsed .sidebar-section-header {
    max-width: none;
    opacity: 1;
  }

  /* Label swap + action hiding are desktop-collapsed-only — the mobile
     drawer always shows the full header. */
  .sidebar-collapsed .sidebar-section-header__label--full,
  .sidebar-collapsed .sidebar-section-header-action {
    display: inline-flex;
  }
  .sidebar-collapsed .sidebar-section-header__label--collapsed {
    display: none;
  }

  /* Rows: restore normal padding (not the compact 12px icon-only padding) */
  .sidebar-collapsed .sidebar-row {
    padding: 6px var(--sp-sm);
  }

  /* Header: restore space-between layout */
  .sidebar-collapsed .sidebar-header {
    justify-content: space-between;
  }

  /* Footer: restore horizontal layout */
  .sidebar-collapsed .sidebar-footer {
    flex-direction: row;
    padding: var(--sp-sm);
    gap: var(--sp-sm);
    align-items: center;
  }

  /* Theme toggle: restore visibility */
  .sidebar-collapsed .sidebar-theme-toggle {
    display: inline-flex;
    max-width: 28px;
    opacity: 1;
  }

  /* Footer user button: restore horizontal layout */
  .sidebar-collapsed .sidebar-footer-user {
    height: auto;
    width: 100%;
    flex-direction: row;
    padding: 0;
  }
}

/* Feed view styles — tabs, item cards, grid, loading/empty/error states. */

/* ---------- Pull-to-refresh indicator ---------- */

/*
 * #feed-ptr-indicator sits as the first child of .feed-items-wrap.
 * Its height is driven by JS during the gesture (inline style). When the
 * gesture completes, JS removes inline styles and Gleam adds --active.
 * The indicator collapses back to height:0 when --active is absent.
 */
.feed-ptr-indicator {
  width: 100%;
  height: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: height 0.25s ease, opacity 0.2s ease, margin-bottom 0.25s ease;
  opacity: 0;
  /* Idle at height:0 the indicator still occupies a flex slot, so the
     wrap's gap rendered a phantom --sp-xl between the header and the
     first card. Cancel that gap while idle; --active restores it so the
     spinner keeps its spacing during a refresh. */
  margin-bottom: calc(-1 * var(--sp-xl));
}

/* JS sets --pulling during the drag; Lustre sets --active while the
   manifest refresh is in flight. Both restore the gap slot the idle
   negative margin cancels. */
.feed-ptr-indicator--pulling {
  margin-bottom: 0;
}

.feed-ptr-indicator--active {
  height: 52px;
  opacity: 1;
  margin-bottom: 0;
}

/* Spinner ring — pure CSS, no external assets. */
.feed-ptr-spinner {
  width: 22px;
  height: 22px;
  border: 2.5px solid var(--border-primary);
  border-top-color: var(--text-secondary);
  border-radius: 50%;
  animation: feed-ptr-spin 0.7s linear infinite;
}

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

/*
 * During the drag phase JS mutates `style.height` and `style.opacity`
 * directly on .feed-ptr-indicator, bypassing the CSS transition so the
 * motion tracks the finger in real time.
 *
 * On release with a confirmed refresh:
 *   1. onRefresh() fires → Lustre queues a re-render with --active
 *   2. One rAF later, JS clears the inline styles
 *   3. Lustre's render has already applied --active (height: 52px), so no
 *      visual jump occurs — the indicator stays pinned at 52px
 *   4. When ManifestRefreshed clears pull_refreshing, Lustre removes --active
 *      and the CSS transition collapses height back to 0
 *
 * `feed-ptr-indicator--ready` is toggled by JS when drag >= threshold.
 * It brightens the spinner to signal "release to refresh".
 */
.feed-ptr-indicator.feed-ptr-indicator--ready .feed-ptr-spinner {
  border-top-color: var(--text-primary);
}

/* Full-page wrapper: hosts the standardized page header (full width,
   own inset) above the column-constrained .feed body. */
.feed-page {
  height: 100%;
  background: var(--bg-secondary);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.feed {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  flex-direction: column;
  padding: var(--sp-xs) var(--sp-xl) 0 var(--sp-xl);
  width: 54vw;
  max-width: 900px;
  margin: 0 auto;
  overflow: hidden;
}

/* Search entry below the header — shares .search-input's chrome (border,
   radius, bg, padding); these overrides make the <a> behave as a static
   pill rather than a live input. */
.feed-search-pill {
  flex: 0 0 auto !important;
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  color: var(--text-tertiary);
  text-decoration: none;
  cursor: pointer;
  margin-bottom: var(--sp-md);
  height: 48px;
}
@media (hover: hover) {
  .feed-search-pill:hover {
  text-decoration: none;
  border-color: var(--border-primary);
}
}

@media (min-width: 769px) {
    .feed-search-pill {
        width: 100%;
        max-width: 800px;
    }
}

/* Mobile: search lives in the bottom action bar — no pill under the header. */
@media (max-width: 768px) {
  .feed-search-pill {
    display: none;
  }
}

/* ---------- Tabs ---------- */

/* Outer row: scrollable chips on the left, pinned search button on the right */
.feed-tabs-row {
  display: flex;
  align-items: center;
  margin-bottom: var(--sp-xl);
  position: relative;
}

/* Scrollable chip strip — search button is NOT inside here */
.feed-tabs {
  display: flex;
  gap: var(--sp-xs);
  align-items: center;
  justify-content: flex-start;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  overflow-y: visible;
  flex-wrap: nowrap;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--sp-3xs); /* prevent clipping on the scroll track */
}
.feed-tabs::-webkit-scrollbar {
  display: none;
}

.feed-tab {
  padding: var(--sp-xs) var(--sp-md);
  border-radius: var(--radius-full);
  border: 1px solid rgba(13, 13, 13, 0.05);
  font-size: var(--text-sm);
  font-weight: 400;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  flex-shrink: 0;
}
@media (hover: hover) {
  .feed-tab:hover {
  color: var(--text-primary);
}
}
.feed-tab-active {
  background: var(--bg-secondary);
  border-color: transparent;
  color: var(--text-primary);
  font-weight: 500;
}
@media (hover: hover) {
  .feed-tab-active:hover {
  background: var(--bg-secondary);
  border-color: transparent;
  color: var(--text-primary);
  font-weight: 500;
}
}

.feed-tab-icon {
  width: 32px;
  height: 32px;
  min-width: 32px;
  flex-shrink: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Search icon anchored to the right of the tab row.
   Sizing/color/radius/hover come from .header-icon-btn. */

/* ---------- Grid ---------- */

.feed-items-wrap {
  flex: 1;
  min-height: 0;
  width: 100%;
  overflow-y: auto;
  /* Explicit x-axis lock: when overflow-y is non-visible, CSS computes
     overflow-x as `auto` unless we say otherwise — on iOS that means the
     container can scroll horizontally if any child is 1px too wide.
     `touch-action: pan-y` tells iOS to only recognise vertical panning here
     so the PTR gesture doesn't open a sideways scroll context. */
  overflow-x: hidden;
  touch-action: pan-y;
  display: flex;
  flex-direction: column;
  gap: var(--sp-xl);
  padding-bottom: var(--sp-xl);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
/* Timeline sections: header + its own masonry grid, stacked in the wrap. */
.feed-section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

/* Desktop: constrain sections (centered by .feed-items-wrap--timeline). */
@media (min-width: 769px) {
  .feed-section {
    width: 100%;
    max-width: 800px;
  }
}

.feed-section-title {
  margin: 0;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
}

.feed-items-wrap::-webkit-scrollbar {
  display: none;
}

.feed-grid {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: var(--sp-xl);
}

.feed-col {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  gap: var(--sp-xl);
}

/* 4-col needs more room */
.feed--cols-4 {
  width: 80vw;
  max-width: 80vw;
}

/* ---------- Card ---------- */

/* Wrapper: inner grid drives the collapse animation (grid-template-rows: 1fr → 0fr).
   No margin-bottom needed — gap on .feed-grid handles row spacing. */
.feed-card-wrapper {
  display: grid;
  grid-template-rows: 1fr;
}

/* Hidden state — card is in the DOM but takes up no space.
   Lustre sets this via feed-card--hidden while the processing toast is
   still running. animateCardFlyIn() removes this class (via Gleam) and
   plays the FLIP fly-in. */
.feed-card--hidden {
  display: none;
}

/* Collapse siblings upward once the card has flown out.
   The 1-minute delay lets the user see the completed state (and undo if
   needed) before the card disappears. Collapse starts just as the flyout
   is finishing: 60s card delay + 230ms into the 260ms flyout. */
.feed-card-wrapper:has(.feed-card--completing) {
  animation: feed-card-collapse 300ms calc(60s + 230ms) ease-in-out forwards;
}

@keyframes feed-card-collapse {
  to {
    grid-template-rows: 0fr;
    margin-bottom: 0;
  }
}

@keyframes feed-card-flyout-up {
  to { transform: translateY(-115%); opacity: 0; }
}

/* All completing cards fly upward — column position isn't knowable via CSS
   selectors in a multi-column layout, so we use a single consistent direction. */
.feed-card--completing {
  animation: feed-card-flyout-up 260ms 60s ease-in forwards;
}

.feed-card {
  width: 100%;
  display: inline-flex;
  flex-direction: column;
  background: var(--bg-primary);
  border: none;
  border-radius: 16px;
  overflow: hidden; /* clips media image to card's rounded corners */
  transition: transform 0.15s ease;
  break-inside: avoid;
  min-height: 0; /* required for grid-template-rows collapse */
}

@media (hover: hover) {
  .feed-card:hover {
  transform: translateY(-1px);
}
}

/* Text cards use the same chrome-free style as photo/bookmark cards.
   When a thumbnail is present it shows above the title just like a photo. */
.feed-card-text {
  color: var(--text-primary);
}

/* The anchor wraps only the media area. The card body (title, author, likes)
   sits as a sibling outside the anchor so the like button doesn't trigger
   modem's anchor-intercepted navigation. */
.feed-card-link {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
  position: relative;
}
@media (hover: hover) {
  .feed-card-link:hover {
  text-decoration: none;
}
}

/* ---------- Media area ---------- */

.feed-card-media {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
  padding: var(--sp-sm);
  color: #ffffff;
  font-size: var(--text-xs);
  overflow: hidden;
  /* No border-radius here — the card's overflow:hidden + border-radius:16px
     clips the media to rounded corners at the top automatically. */
}

.feed-card-media-photo {
  padding: 0;
}
.feed-card-media-voice {
  aspect-ratio: 4 / 3;
}
.feed-card-media-bookmark {
  aspect-ratio: 16 / 9;
  min-width: 100%;
  min-height: 120px;
}

/* Tiles with no media (calendar tiles; text tiles without a thumbnail)
   reserve a slim band when a manifest badge is present, so the
   absolute-positioned `.feed-badge-pill` has room to sit without overlapping
   the card body. The pill is ~32px tall plus its top inset, so 48px clears it. */
.feed-card-badge-spacer {
  height: 48px;
  width: 100%;
}

.feed-card-photo-placeholder {
  width: 100%;
  height: 100%;
}

/* Deterministic gradients for placeholders */
.feed-card-gradient-1 { background: linear-gradient(135deg, #667eea, #764ba2); }
.feed-card-gradient-2 { background: linear-gradient(135deg, #f093fb, #f5576c); }
.feed-card-gradient-3 { background: linear-gradient(135deg, #4facfe, #00f2fe); }
.feed-card-gradient-4 { background: linear-gradient(135deg, #43e97b, #38f9d7); }
.feed-card-gradient-5 { background: linear-gradient(135deg, #fa709a, #fee140); }
.feed-card-gradient-6 { background: linear-gradient(135deg, #30cfd0, #330867); }

/* ── Shared square media thumb (ui/media_thumb.gleam) ─────────────────
   Per-category art for compact 1:1 slots (Activity rows). The host sets
   the box size and radius on `.media-thumb`; these style the variants. */
.media-thumb-img {
  object-fit: cover;
  display: block;
}

.media-thumb-voice {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3xs);
  color: #fff;
}

.media-thumb-voice-icon {
  display: flex;
}

.media-thumb-voice-duration {
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1;
}

.media-thumb-calendar {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  background: var(--fill-elevated-quaternary);
  color: var(--text-secondary);
}

/* Date badge ("JUN" / "26") — red uppercase month over the day numeral,
   same treatment as the feed item cards' calendar tile. */
.media-thumb-cal-month {
  font-size: 13px;
  font-weight: 600;
  line-height: 16px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent-red);
}

.media-thumb-cal-day {
  font-size: 30px;
  font-weight: 500;
  line-height: 34px;
  color: var(--text-primary);
}

/* Waveform slot: the <waveform-tile> inside fills the square (its own
   .wf-tile class pins 1:1 and draws in currentColor). */
.media-thumb-waveform {
  background: transparent;
}

.feed-card-voice-duration,
.feed-card-bookmark-site {
  background: rgba(0, 0, 0, 0.5);
  padding: var(--sp-3xs) var(--sp-xs);
  border-radius: var(--radius-md);
  backdrop-filter: blur(4px);
  font-weight: 500;
}

.feed-card-voice-icon {
  position: absolute;
  top: var(--sp-sm);
  left: var(--sp-sm);
  background: rgba(0, 0, 0, 0.5);
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(4px);
  color: #fff;
}

/* ---------- Header (avatar + name) ---------- */

.feed-card-header {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-xs);
  padding: var(--sp-sm);
  text-decoration: none;
  color: var(--text-tertiary);
  min-width: 0;
  overflow: hidden;
}
@media (hover: hover) {
  .feed-card-header:hover {
  text-decoration: none;
  color: var(--text-primary);
}
}

.feed-card-header-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.feed-card-header-name {
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: inherit;
}

.feed-card-header-timestamp {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  line-height: 1.3;
}

/* ---------- Body (title row + footer) ---------- */

.feed-card-body {
  padding: var(--sp-sm) var(--sp-xs);
  width: 100%;
}

/* Title row: completion checkbox + title link side by side */
.feed-card-title-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-xs);
}

/* Title link fills remaining width */
.feed-card-title-link {
  flex: 1;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
@media (hover: hover) {
  .feed-card-title-link:hover {
  text-decoration: none;
}
}

.feed-card-title {
  font-size: 14px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Completed state: struck-through, lighter weight and color */
.feed-card-title--done {
  text-decoration: line-through;
  font-weight: 400;
  color: var(--text-tertiary);
}

/* Event time line shown above the title on calendar tiles. Sits inside
   the title link so it inherits the same hit area as the title. */
.feed-card-event-time {
  display: block;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-tertiary);
  margin-bottom: var(--sp-3xs);
}

/* Calendar metadata row: source-calendar name badge under the title.
   Lives inside `.feed-card-title-link` so it inherits the title's
   left indent automatically. */
.feed-card-meta-row {
  display: flex;
  margin-top: var(--sp-3xs);
}
.feed-card-meta-badge {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  background: var(--bg-secondary);
  padding: var(--sp-3xs) var(--sp-xs);
  border-radius: var(--radius-md);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Completion checkbox button */
.feed-card-complete-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 1px; /* optical alignment with first line of title */
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--text-tertiary);
  font-family: inherit;
}
@media (hover: hover) {
  .feed-card-complete-btn:hover {
  color: var(--text-primary);
}
}
.feed-card-complete-btn--done {
  color: var(--color-primary, var(--accent));
}
@media (hover: hover) {
  .feed-card-complete-btn--done:hover {
  color: var(--color-primary, var(--accent));
}
}

/* Footer: timestamp left, engagement actions right */
/* Please-agent comment preview on the tile. */
.feed-card-agent {
  margin: var(--sp-xs) 0 0;
  padding: var(--sp-xs) var(--sp-sm);
  background: var(--surface-secondary, rgba(127, 127, 127, 0.06));
  border-radius: var(--radius-sm, 8px);
}
.feed-card-agent-label {
  display: block;
  font-size: var(--text-2xs, 0.625rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent-blue);
  margin-bottom: 2px;
}
.feed-card-agent-text {
  margin: 0;
  font-size: var(--text-xs);
  line-height: 1.4;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.feed-card-agent-pending {
  font-size: var(--text-xs);
  font-style: italic;
  color: var(--text-tertiary);
}

.feed-card-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-top: var(--sp-sm);
}

.feed-card-footer-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
}

/* Comments count */
.feed-card-comments {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.feed-card-comments-icon {
  display: inline-flex;
  align-items: center;
}

.feed-card-comments .icon {
  width: 13px;
  height: 13px;
}

.feed-card-comments-count {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* Bookmark button */
.feed-card-bookmark-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  color: var(--text-secondary);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
}
@media (hover: hover) {
  .feed-card-bookmark-btn:hover {
  color: var(--text-primary);
}
}
.feed-card-bookmark-btn--active {
  color: var(--color-primary, var(--accent));
}
@media (hover: hover) {
  .feed-card-bookmark-btn--active:hover {
  color: var(--color-primary, var(--accent));
}
}
.feed-card-bookmark-btn .icon {
  width: 14px;
  height: 14px;
}

/* Likes */
.feed-card-likes {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  color: var(--text-secondary);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
}
@media (hover: hover) {
  .feed-card-likes:hover {
  color: var(--text-primary);
}
}
.feed-card-likes-active {
  color: var(--accent-red);
}
@media (hover: hover) {
  .feed-card-likes-active:hover {
  color: var(--accent-red);
}
}

.feed-card-likes-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-secondary);
}

.feed-card-likes-count {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.feed-card-likes .icon {
  width: 13px;
  height: 13px;
}

/* ---------- States ---------- */

.feed-loading,
.feed-empty,
.feed-error {
  padding: var(--sp-xxl) 0;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  text-align: center;
}
.feed-error {
  color: var(--accent-red);
}

.feed-load-more {
  display: block;
  margin: 0 auto;
}

/* Real images in cards — absolutely positioned so they fill the media
   container without competing with overlay siblings (e.g. bookmark site
   badge) in the flex layout. The parent's overflow:hidden + border-radius
   handles corner clipping. */
.feed-card-img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.feed-card-media-text {
  aspect-ratio: 1;
  overflow: hidden;
  width: 100%;
  padding: 0;
}

/* ---------- Badge pill ---------- */

.feed-badge-pill {
  position: absolute;
  top: var(--sp-sm);
  left: var(--sp-sm);
  z-index: 2;
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  background: var(--bg-70);
  color: var(--text-primary);
  /* border: 1px solid var(--border-primary); */
  width: fit-content;
  backdrop-filter: blur(12px);
}

.feed-card-calendar .feed-badge-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
}

.feed-card-calendar .feed-badge-pill::before {
  content: '';
  display: inline-block;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 xmlns=%22http://www.w3.org/2000/svg%22%3E %3Cg clip-path=%22url(%23clip0_17956_8822)%22%3E %3Cpath d=%22M8.94719 14.0525C8.94751 10.1868 12.081 7.05296 15.9467 7.05276C19.8124 7.05282 22.9459 10.1866 22.9464 14.0522C22.9461 17.918 19.8119 21.0525 15.9461 21.0525C12.0806 21.052 8.94744 17.918 8.94719 14.0525ZM13.267 9.41761C11.9629 10.303 11.1607 11.6717 10.9324 13.1377L10.7741 12.8935C10.5787 12.5917 10.1817 12.5092 9.88702 12.7092C9.59259 12.9094 9.51248 13.3158 9.7076 13.6177L10.9007 15.4642C11.0961 15.7657 11.4934 15.847 11.788 15.6471L13.5891 14.4244C13.8837 14.2242 13.9646 13.8173 13.7693 13.5154C13.5739 13.2139 13.1768 13.1313 12.8822 13.3311L12.1495 13.8272C12.2035 12.532 12.8405 11.2816 13.9744 10.5117C15.8561 9.2345 18.3932 9.76175 19.64 11.6895C20.8862 13.6174 20.3705 16.2158 18.4891 17.4931C17.1863 18.3773 15.569 18.3973 14.282 17.6836C13.9712 17.5114 13.583 17.6298 13.4145 17.9479C13.2463 18.2664 13.3615 18.6648 13.6723 18.8373C15.3588 19.7727 17.4837 19.7496 19.1964 18.5871C21.6675 16.9096 22.3443 13.4966 20.7073 10.9648C19.0698 8.43296 15.7382 7.74014 13.267 9.41761ZM4.04413 3.97441C3.97353 3.56673 4.24666 3.1788 4.65408 3.1075C5.0622 3.03673 5.45075 3.31015 5.52152 3.71827L5.68865 4.67945L11.0449 3.74977L10.8786 2.78805C10.808 2.38009 11.0814 1.99142 11.4894 1.92061C11.897 1.85032 12.2857 2.1239 12.3568 2.53138L12.5228 3.49445L13.4054 3.34309C14.7569 3.10909 15.9553 4.10536 16.1831 5.41884C16.2537 5.82678 15.9789 6.21513 15.571 6.28601C15.1633 6.35622 14.7752 6.08241 14.7044 5.6747C14.5978 5.06188 14.0845 4.74664 13.6602 4.82021L3.61189 6.5628C3.18756 6.63644 2.81138 7.10668 2.9177 7.71982L3.16715 9.16415L8.42199 8.2759C8.82999 8.20714 9.21678 8.48097 9.28618 8.88883C9.35514 9.29698 9.08047 9.6842 8.67244 9.75355L3.41029 10.6431L4.42524 16.4958C4.50337 16.9448 4.94649 17.2691 5.43981 17.1837L7.85129 16.7655C8.25931 16.6948 8.64792 16.9682 8.71873 17.3762C8.78909 17.7839 8.51635 18.1721 8.10878 18.2431L5.69595 18.6611C4.41368 18.8831 3.172 18.0443 2.94784 16.752L1.71726 9.65628C1.70941 9.61091 1.70563 9.56528 1.706 9.52082L1.43841 7.97486C1.21116 6.6617 2.00349 5.31987 3.3544 5.08512L4.21044 4.93612L4.04413 3.97441Z%22 fill=%22black%22/%3E %3C/g%3E %3Cdefs%3E %3CclipPath id=%22clip0_17956_8822%22%3E %3Crect width=%2224%22 height=%2224%22 fill=%22white%22/%3E %3C/clipPath%3E %3C/defs%3E %3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 xmlns=%22http://www.w3.org/2000/svg%22%3E %3Cg clip-path=%22url(%23clip0_17956_8822)%22%3E %3Cpath d=%22M8.94719 14.0525C8.94751 10.1868 12.081 7.05296 15.9467 7.05276C19.8124 7.05282 22.9459 10.1866 22.9464 14.0522C22.9461 17.918 19.8119 21.0525 15.9461 21.0525C12.0806 21.052 8.94744 17.918 8.94719 14.0525ZM13.267 9.41761C11.9629 10.303 11.1607 11.6717 10.9324 13.1377L10.7741 12.8935C10.5787 12.5917 10.1817 12.5092 9.88702 12.7092C9.59259 12.9094 9.51248 13.3158 9.7076 13.6177L10.9007 15.4642C11.0961 15.7657 11.4934 15.847 11.788 15.6471L13.5891 14.4244C13.8837 14.2242 13.9646 13.8173 13.7693 13.5154C13.5739 13.2139 13.1768 13.1313 12.8822 13.3311L12.1495 13.8272C12.2035 12.532 12.8405 11.2816 13.9744 10.5117C15.8561 9.2345 18.3932 9.76175 19.64 11.6895C20.8862 13.6174 20.3705 16.2158 18.4891 17.4931C17.1863 18.3773 15.569 18.3973 14.282 17.6836C13.9712 17.5114 13.583 17.6298 13.4145 17.9479C13.2463 18.2664 13.3615 18.6648 13.6723 18.8373C15.3588 19.7727 17.4837 19.7496 19.1964 18.5871C21.6675 16.9096 22.3443 13.4966 20.7073 10.9648C19.0698 8.43296 15.7382 7.74014 13.267 9.41761ZM4.04413 3.97441C3.97353 3.56673 4.24666 3.1788 4.65408 3.1075C5.0622 3.03673 5.45075 3.31015 5.52152 3.71827L5.68865 4.67945L11.0449 3.74977L10.8786 2.78805C10.808 2.38009 11.0814 1.99142 11.4894 1.92061C11.897 1.85032 12.2857 2.1239 12.3568 2.53138L12.5228 3.49445L13.4054 3.34309C14.7569 3.10909 15.9553 4.10536 16.1831 5.41884C16.2537 5.82678 15.9789 6.21513 15.571 6.28601C15.1633 6.35622 14.7752 6.08241 14.7044 5.6747C14.5978 5.06188 14.0845 4.74664 13.6602 4.82021L3.61189 6.5628C3.18756 6.63644 2.81138 7.10668 2.9177 7.71982L3.16715 9.16415L8.42199 8.2759C8.82999 8.20714 9.21678 8.48097 9.28618 8.88883C9.35514 9.29698 9.08047 9.6842 8.67244 9.75355L3.41029 10.6431L4.42524 16.4958C4.50337 16.9448 4.94649 17.2691 5.43981 17.1837L7.85129 16.7655C8.25931 16.6948 8.64792 16.9682 8.71873 17.3762C8.78909 17.7839 8.51635 18.1721 8.10878 18.2431L5.69595 18.6611C4.41368 18.8831 3.172 18.0443 2.94784 16.752L1.71726 9.65628C1.70941 9.61091 1.70563 9.56528 1.706 9.52082L1.43841 7.97486C1.21116 6.6617 2.00349 5.31987 3.3544 5.08512L4.21044 4.93612L4.04413 3.97441Z%22 fill=%22black%22/%3E %3C/g%3E %3Cdefs%3E %3CclipPath id=%22clip0_17956_8822%22%3E %3Crect width=%2224%22 height=%2224%22 fill=%22white%22/%3E %3C/clipPath%3E %3C/defs%3E %3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-position: center;
          mask-position: center;
}

/* ---------- Mobile header (hidden on desktop) ---------- */

/* ---------- Mobile FAB (hidden on desktop) ---------- */

.feed-fab {
  display: none;
}

/* ---------- Desktop: search btn inside tab row ---------- */

/* Pinned search button — sits outside the scrollable chip strip so it never
   scrolls out of view. A gradient pseudo-element on the left edge creates a
   fade-out shadow that signals content is scrolling underneath it. */
.feed-search-btn-desktop {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  position: relative;
  padding-left: var(--sp-xs);
}
.feed-search-btn-desktop::before {
  content: '';
  position: absolute;
  right: 100%;
  top: -8px;
  bottom: -8px;
  width: 48px;
  background: linear-gradient(to right, transparent, var(--bg-body));
  pointer-events: none;
}

/* ── Mid-desktop (769px – 1440px) ────────────────────────────────────── */

@media (min-width: 769px) and (max-width: 1440px) {
  .feed {
    width: 68vw;
    max-width: 68vw;
    padding-left: var(--sp-xs);
    padding-right: var(--sp-xs);
  }
}

/* ── Mobile responsive (≤ 768px) ─────────────────────────────────────── */

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (max-width: 768px) {
  .feed {
    width: 100%;
    max-width: 100vw;
    padding: var(--sp-xl) var(--sp-md)  var(--sp-sm);
    overflow: hidden;
  }

  .feed-items-wrap {
    gap: var(--sp-xxl);
  }

  /* Tighter gap on mobile */
  .feed-grid {
    gap: var(--sp-md);
  }

  .feed-col {
    gap: var(--sp-md);
  }

  /* Reset 4-col container override on mobile */
  .feed--cols-4 {
    width: 100%;
    max-width: 100vw;
  }

  .feed-card-title {
    font-size: 13px;
  }

  /* Hide the desktop search button — mobile has it in the header */
  .feed-search-btn-desktop {
    display: none;
  }

      /* On mobile the tabs row margin is tighter */
  .feed-tabs-row {
    margin-bottom: var(--sp-xs);
  }

  /* Shift the tab strip to align with the screen edge on mobile */
  .feed-tabs {
    padding-left: var(--sp-md);
  }

      /* Notifications entry point — replaces the old top search icon now that
     search lives in the bottom mobile-action-bar (mobile_action_bar.gleam). */
  /* .feed-header-notifications sizing/color/radius/hover come from
     .header-icon-btn — only the badge-dot anchor is kept here. */
  .feed-header-notifications {
    position: relative;
  }

  .feed-header-notifications-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 8px;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--accent-red);
    border: 2px solid var(--bg-body);
    box-sizing: content-box;
    pointer-events: none;
  }

  /* Extra bottom padding so content isn't hidden behind the mobile
     action bar (search + create), fixed at the bottom of the viewport. */
  .feed-items-wrap {
    padding-bottom: 88px;
  }

  /* On Timeline the bar floats over the scrolling item list — fixed to
     the viewport bottom, unlike the Please AI chat's in-flow placement
     (see `.chat .mobile-action-bar` in chat.css). */
  .feed .mobile-action-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
  }
}


/* Shared by the reminders list and search — see ui/timeline_list. */
/* Timeline reminder rows (iOS TimelineListRow parity).

   Lifted out of feed.css: the reminders list and search now render the same
   rows through ui/timeline_list, so this is a shared component's stylesheet,
   not a feed-specific one. Same move .due-pill made when it stopped being
   feed-only. */


/* The Timeline tab swaps the masonry grid for a vertical list of reminder
   rows on the iOS canvas color. Only the Timeline tab gets this class —
   Saved/ForYou/Following/search/profile keep the grid. */
.feed-items-wrap--timeline {
  align-items: center;
}

.tl-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm); /* 12px between rows — distinct from the 16px
                         (--sp-md) title→content gap on .feed-section */
}

/* Card: [completion circle 22] [content column] [56×56 thumb], top-aligned.
   16px padding + radius, feed-card background, no border/shadow. */
.tl-row {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 12px;
  border-radius: 16px;
  background: var(--bg-primary);
  border: none;
  box-shadow: none;
  transition: background-color 150ms ease;
}

/* Whole-card affordance: animated background tint on hover. (Solid color
   → solid color interpolates fine through the tokens — unlike gradients,
   which is why the Done button needed the opacity-overlay trick.) */
@media (hover: hover) {
  .tl-row:hover {
  background: var(--fill-elevated-secondary);
}
}

/* Never underline the card's nested anchors on hover (overrides the
   global `a:hover` underline from base.css). */
.tl-row a {
  text-decoration: none;
}
@media (hover: hover) {
  .tl-row a:hover {
  text-decoration: none;
}
}

/* Stretched link: the content anchor's ::after covers the whole card, so
   any tap that isn't on a nested action navigates to the reminder's
   detail view. Nested actions (completion circle) sit above it. */
.tl-row-content::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 16px;
}


/* 1.5px pastel gradient border while the agent comment is pending.
   Painted by an overlay pseudo-element masked to a ring so the card
   background stays var(--feed-card-bg). */
.tl-row--pending::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 16px;
  padding: 1.5px;
  background: var(--gradient-pastel);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
          mask-composite: exclude;
  pointer-events: none;
}

/* Completion circle: hollow ring (ui-borders) unchecked, filled check
   (text-secondary) when done. Rendered as a <button> when tappable
   (reminder present) or a <span> otherwise. */
/* Layout-only overrides for the shared completion circle
   (ui/completion_circle → .nr-completion-circle states) inside a
   timeline row: kill the heading's first-line margin and keep the
   unchecked ring in the row's border color. */
.tl-row-check.nr-completion-circle {
  margin-top: 4px;
  border-color: var(--ui-borders);
  /* Above the stretched card link so the toggle stays tappable. */
  position: relative;
  z-index: 1;
}
.tl-row-check.nr-completion-circle--done {
  border-color: transparent;
}

/* Content column: 12px between blocks (title-group / due pill / agent row). */
.tl-row-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  text-decoration: none;
  color: inherit;
}

/* Title → calendar meta sit 2px apart, tighter than the block gap. */
.tl-row-title-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  align-self: stretch;
}

.tl-row-title {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 400;
  line-height: 140%;
  letter-spacing: -0.43px;
  color: var(--tl-text-primary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tl-row-title--done {
  text-decoration: line-through;
}

.tl-row-cal-meta {
  font-size: 13px;
  font-weight: 400;
  line-height: 130%;
  letter-spacing: -1.5%;
  color: var(--text-secondary);
}

/* Due pill (`ui/due_pill.gleam`) moved to `styles/pill.css` — it's a
   shared component, not feed-specific. */

/* AI comment row: 16px agent glyph + 2-line preview, text-secondary. */
.tl-row-agent {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  align-self: stretch;
  color: var(--text-secondary-vibrant);
}
.tl-row-agent-glyph {
  display: inline-flex;
  flex-shrink: 0;
  /* Optically align the 16px glyph with the first 20px text line. */
  margin-top: 2px;
}
.tl-row-agent-text {
  margin: 0;
  font-size: 13px;
  font-weight: 400;
  line-height: 180%;
  letter-spacing: -0.08px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Pending state: pastel-gradient glyph + two shimmer bars instead of text. */
.tl-row-agent--pending .tl-row-agent-glyph .icon {
  background: var(--gradient-pastel);
}
.tl-row-agent-shimmer {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: 2px;
}
.tl-shimmer-bar {
  height: 10px;
  border-radius: 5px;
  background: var(--fill-elevated-tertiary);
  animation: tl-shimmer 1.2s ease-in-out infinite;
}
.tl-shimmer-bar--short {
  width: 140px;
}
@keyframes tl-shimmer {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* Thumbnail slot: 56×56, 12px radius, cover. */
.tl-row-thumb {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 12px;
  overflow: hidden;
  display: block;
}
.tl-thumb-img,
.tl-thumb-media {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Calendar month/day badge (calendar items without artwork). */
.tl-thumb-calendar {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--fill-elevated-secondary);
}
.tl-thumb-cal-month {
  font-size: 11px;
  font-weight: 600;
  line-height: 13px;
  text-transform: uppercase;
  color: var(--accent-red);
}
.tl-thumb-cal-day {
  font-size: 22px;
  font-weight: 500;
  line-height: 26px;
  color: var(--text-primary-vibrant);
}

@media (max-width: 768px) {
  .feed-section {
    width: 100%;
  }
  .tl-list {
    width: 100%;
  }
}

/* Detail screen — single item view with header, body, and right-side rail. */

.detail {
  display: flex;
  flex-direction: column;
  height: 100vh;   /* fallback */
  height: 100dvh;  /* dynamic viewport */
  overflow: hidden;
  position: relative; /* stacking context for the nav toast */
}

.detail-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 var(--sp-xl);
}

.detail-content {
  display: flex;
  flex-direction: column;
  width: 64vw;
  max-width: 720px;
  margin: 0 auto;
}

/* Header: full width with border below, outside the grid. */
.detail-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-md) var(--sp-xl);
  flex-shrink: 0;
}

.detail-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-md);
}

.detail-meta-nav {
  display: flex;
  gap: var(--sp-3xs);
}

/* Sidebar toggle: hidden on desktop, replaces back button on mobile */
.detail-header-sidebar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  color: var(--text-tertiary);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 0;
}
.detail-header-sidebar-toggle:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}

.detail-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  color: var(--text-tertiary);
  text-decoration: none;
  font-size: var(--text-lg);
}
.detail-back:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
  text-decoration: none;
}

.detail-author-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
}

.detail-author-avatar-link {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  text-decoration: none;
  color: inherit;
}

.detail-author-info {
  display: flex;
  flex-direction: column;
}

.detail-author-name {
  font-size: var(--text-lg);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.detail-author-timestamp {
  font-size: var(--text-base);
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--text-secondary);
}

/* keep .detail-author-type selectable for any other consumers */
.detail-author-type {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 500;
}

.detail-archived-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text-secondary);
  background: var(--bg-tertiary-inverse);
  font-size: var(--text-xs);
  padding: 2px var(--sp-xs);
  border-radius: var(--radius-full);
}

.detail-follow-btn {
  padding: 2px var(--sp-sm);
  border-radius: var(--radius-full);
  border: 1px solid var(--bg-secondary);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
}
.detail-follow-btn:hover {
  opacity: 0.9;
}

.detail-follow-btn-following {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-primary);
}
.detail-follow-btn-following:hover {
  border-color: var(--accent-red);
  color: var(--accent-red);
}

.detail-nav-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  font-size: var(--text-xl);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.detail-nav-btn:hover {
  background: var(--bg-secondary);
  color: var(--text-primary);
}
.detail-nav-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}
.detail-nav-btn:disabled:hover {
  background: transparent;
  color: var(--text-quarternary);
}

.detail-nav-menu {
  margin-left: var(--sp-xs);
  font-size: var(--text-2xl);
}

/* Wrapper for the ellipsis menu button + dropdown */
.detail-menu-wrapper {
  position: relative;
}

/* Ellipsis dropdown menu */
.detail-menu-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 50;
  margin-top: var(--sp-3xs);
  padding: var(--sp-3xs);
  background: var(--bg-primary);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  min-width: 240px;
  display: flex;
  flex-direction: column;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.detail-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  width: 100%;
  padding: var(--sp-xs) var(--sp-sm);
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: inherit;
  text-align: left;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.detail-menu-item:hover {
  background: var(--bg-subtle);
}

.detail-menu-item-danger {
  color: var(--accent-red);
}
.detail-menu-item-danger:hover {
  background: var(--danger-subtle, var(--bg-subtle));
}

/* Expandable visibility section (iOS-style: header row + options). */
.detail-menu-visibility {
  display: flex;
  flex-direction: column;
}
.detail-menu-vis-spacer {
  flex: 1;
}
.detail-menu-vis-header > svg:last-child {
  color: var(--text-tertiary);
}
.detail-menu-vis-option {
  align-items: flex-start;
  padding-left: var(--sp-md);
  gap: var(--sp-sm);
}
.detail-menu-vis-option > svg:first-child {
  margin-top: 2px;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.detail-menu-vis-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.detail-menu-vis-label {
  font-size: var(--text-sm);
  color: var(--text-primary);
}
.detail-menu-vis-desc {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}
.detail-menu-vis-check {
  margin-left: auto;
  padding-left: var(--sp-xs);
  color: var(--accent);
  flex-shrink: 0;
  align-self: center;
}

/* Delete confirmation dialog */
.detail-confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
}
.detail-confirm-dialog {
  background: var(--bg);
  border-radius: var(--radius-lg);
  padding: var(--sp-xl);
  max-width: 360px;
  width: 90%;
  box-shadow: var(--shadow-lg, 0 8px 32px rgba(0,0,0,.2));
}
.detail-confirm-title {
  font-weight: 600;
  font-size: 1.1rem;
  margin: 0 0 var(--sp-xs);
}
.detail-confirm-body {
  color: var(--text-tertiary);
  margin: 0 0 var(--sp-lg);
  font-size: 0.9rem;
}
.detail-confirm-actions {
  display: flex;
  gap: var(--sp-sm);
  justify-content: flex-end;
}
.detail-confirm-btn {
  padding: var(--sp-xs) var(--sp-md);
  border-radius: var(--radius-md);
  border: none;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
}
.detail-confirm-cancel {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
.detail-confirm-cancel:hover {
  background: var(--bg-hover, var(--bg-subtle));
}
.detail-confirm-delete {
  background: var(--accent-red);
  color: white;
}
.detail-confirm-delete:hover {
  opacity: 0.9;
}

/* Body column */
.detail-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-lg);
  min-width: 0;
}

.detail-hero {
  width: 100%;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.detail-hero-photo,
.detail-hero-voice,
.detail-hero-bookmark {
  width: -webkit-fill-available;
  height: auto;
  aspect-ratio: 4 / 3;
  
}

.detail-hero-text {
  aspect-ratio: 1;
  max-width: 400px;
}

.detail-hero-img {
  width: -webkit-fill-available;
  display: block;
  height: auto;
  object-fit: cover;
}

.detail-hero-zoomable {
  cursor: zoom-in;
  transition: opacity 120ms ease;
}

.detail-hero-zoomable:hover {
  opacity: 0.9;
}

.detail-hero-text,
.detail-hero-photo,
.detail-hero-voice,
.detail-hero-bookmark,
.detail-hero-img {
  border-radius: var(--radius-lg);
}

.detail-hero-voice .detail-hero-play-icon,
.detail-hero-voice .detail-hero-duration,
.detail-hero-bookmark .detail-hero-site,
.detail-hero-bookmark .detail-hero-external {
  color: var(--text-primary-inverse);
}

.detail-hero-play-icon {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  backdrop-filter: blur(8px);
}

/* Click-to-preview thumbnail for a single video capture (view_hero.gleam's
   `video_hero`) — same visual footprint as the plain photo hero, plus a
   centered play badge (reusing `.detail-hero-play-icon`'s look) signalling
   the tap opens the fullscreen video viewer instead of playing inline. */
.detail-hero-video-btn {
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}

.detail-hero-video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.45);
  color: var(--text-primary-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(8px);
}

.detail-hero-duration {
  position: absolute;
  bottom: var(--sp-sm);
  right: var(--sp-sm);
  background: rgba(0, 0, 0, 0.5);
  padding: var(--sp-3xs) var(--sp-xs);
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  backdrop-filter: blur(4px);
}

.detail-hero-site {
  position: absolute;
  top: var(--sp-sm);
  left: var(--sp-sm);
  background: rgba(0, 0, 0, 0.45);
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  backdrop-filter: blur(4px);
}

.detail-hero-external {
  position: absolute;
  top: var(--sp-sm);
  right: var(--sp-sm);
  font-size: var(--text-xl);
  background: rgba(0, 0, 0, 0.45);
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
}

.detail-text-content {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}

.detail-title {
  font-size: var(--text-3xl);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.2;
  margin: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}

.detail-title-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-xs);
  flex-wrap: wrap;
}

.detail-title-editable {
  background: transparent;
  border: none;
  resize: none;
  width: 100%;
  padding: 0;
  font-family: var(--font-sans);
  color: inherit;
  field-sizing: content;
  max-height: calc(3 * 1.25em);
  overflow: hidden;
  outline: none;
}

.detail-save-indicator {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  white-space: nowrap;
}

.detail-save-indicator-saved {
  color: var(--accent-green);
}

.detail-save-indicator-failed {
  color: var(--accent-red);
}

.detail-divider {
  border: none;
  border-top: 1px solid var(--border-primary);
  margin: var(--sp-md) 0;
}

.detail-description {
  font-size: var(--text-base);
  color: var(--text-tertiary);
  line-height: 1.6;
  margin: 0;
}

/* Inline images inside a note body (markdown or the Milkdown editor) — keep them
   modestly sized instead of rendering at full natural resolution. */
.detail-description img,
.detail-body-editor img {
  max-width: 100%;
  max-height: 240px;
  width: auto;
  height: auto;
  border-radius: var(--radius-md, 12px);
  object-fit: contain;
}

.detail-description p {
  margin: 0 0 var(--sp-xs) 0;
}
.detail-description p:last-child {
  margin-bottom: 0;
}

.detail-description img {
  max-width: 100%;
  height: auto;
}

.detail-description h1,
.detail-description h2,
.detail-description h3,
.detail-description h4 {
  font-weight: 600;
  color: var(--text-primary);
  margin: var(--sp-sm) 0 var(--sp-3xs);
}
.detail-description h1 { font-size: var(--text-xl); }
.detail-description h2 { font-size: var(--text-lg); }
.detail-description h3 { font-size: var(--text-base); }
.detail-description h4 { font-size: var(--text-sm); }

.detail-description ul,
.detail-description ol {
  margin: var(--sp-xs) 0;
  padding-left: var(--sp-lg);
}
.detail-description ul { list-style: disc; }
.detail-description ol { list-style: decimal; }
.detail-description li { margin-bottom: var(--sp-3xs); }

/* ── Task list shared icon variables ────────────────────────────────────── */
/* SVG data URIs for the three checklist icons (checkbox/empty/hollow,
   checkbox/checked/filled, checklist/drag). Used as CSS mask-image so the
   icons inherit currentColor via background-color. */

:root {
  --icon-task-unchecked: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E %3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21 18.6504C21 18.9179 21.0011 19.1639 20.9844 19.3682C20.967 19.5812 20.9268 19.8147 20.8096 20.0449C20.6418 20.374 20.374 20.6418 20.0449 20.8096C19.8147 20.9268 19.5812 20.967 19.3682 20.9844C19.1639 21.0011 18.9179 21 18.6504 21H5.34961C5.08211 21 4.83612 21.0011 4.63184 20.9844C4.41878 20.967 4.18529 20.9268 3.95508 20.8096C3.62597 20.6418 3.35817 20.374 3.19043 20.0449C3.07313 19.8147 3.03402 19.5812 3.0166 19.3682C2.99991 19.1639 3 18.9179 3 18.6504V5.34961C3 5.08211 2.99991 4.83612 3.0166 4.63184C3.03403 4.4188 3.07314 4.18526 3.19043 3.95508C3.35818 3.62597 3.62597 3.35818 3.95508 3.19043C4.18526 3.07314 4.4188 3.03403 4.63184 3.0166C4.83612 2.99991 5.08211 3 5.34961 3H18.6504C18.9179 3 19.1639 2.99991 19.3682 3.0166C19.5812 3.03402 19.8147 3.07313 20.0449 3.19043C20.374 3.35817 20.6418 3.62597 20.8096 3.95508C20.9268 4.18529 20.967 4.41878 20.9844 4.63184C21.0011 4.83612 21 5.08211 21 5.34961V18.6504ZM19.5 5.34961C19.5 5.05745 19.4998 4.88297 19.4893 4.75391C19.4794 4.63384 19.4643 4.62034 19.4727 4.63672C19.4487 4.58968 19.4103 4.55131 19.3633 4.52734C19.3797 4.53569 19.3662 4.52058 19.2461 4.51074C19.1171 4.5002 18.9426 4.5 18.6504 4.5H5.34961C5.05745 4.5 4.88297 4.5002 4.75391 4.51074C4.63384 4.52058 4.62034 4.53569 4.63672 4.52734C4.58968 4.55131 4.55131 4.58968 4.52734 4.63672C4.53569 4.62034 4.52058 4.63384 4.51074 4.75391C4.5002 4.88297 4.5 5.05745 4.5 5.34961V18.6504C4.5 18.9426 4.5002 19.1171 4.51074 19.2461C4.52058 19.3662 4.53569 19.3797 4.52734 19.3633C4.55131 19.4103 4.58968 19.4487 4.63672 19.4727C4.62034 19.4643 4.63384 19.4794 4.75391 19.4893C4.88297 19.4998 5.05745 19.5 5.34961 19.5H18.6504C18.9426 19.5 19.1171 19.4998 19.2461 19.4893C19.3662 19.4794 19.3797 19.4643 19.3633 19.4727C19.4103 19.4487 19.4487 19.4103 19.4727 19.3633C19.4643 19.3797 19.4794 19.3662 19.4893 19.2461C19.4998 19.1171 19.5 18.9426 19.5 18.6504V5.34961Z' fill='black'/%3E %3C/svg%3E");
  --icon-task-checked: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E %3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M18.6768 3C18.934 2.99998 19.1706 3.00046 19.3682 3.0166C19.5812 3.03402 19.8147 3.07313 20.0449 3.19043C20.374 3.35818 20.6418 3.62598 20.8096 3.95508C20.9268 4.1853 20.967 4.41878 20.9844 4.63184C21.0005 4.82952 21 5.06674 21 5.32422V18.6758C21 18.9333 21.0005 19.1705 20.9844 19.3682C20.967 19.5812 20.9268 19.8147 20.8096 20.0449C20.6418 20.374 20.374 20.6418 20.0449 20.8096C19.8147 20.9268 19.5812 20.967 19.3682 20.9844C19.1705 21.0005 18.9333 21 18.6758 21H5.32324C5.06597 21 4.82939 21.0005 4.63184 20.9844C4.41878 20.967 4.1853 20.9268 3.95508 20.8096C3.62598 20.6418 3.35818 20.374 3.19043 20.0449C3.07314 19.8147 3.03402 19.5812 3.0166 19.3682C3.00045 19.1705 2.99998 18.9333 3 18.6758V5.32422C2.99998 5.06676 3.00045 4.82951 3.0166 4.63184C3.03403 4.4188 3.07314 4.18527 3.19043 3.95508C3.35818 3.62598 3.62598 3.35818 3.95508 3.19043C4.18527 3.07314 4.4188 3.03403 4.63184 3.0166C4.82951 3.00045 5.06675 2.99998 5.32422 3H18.6768ZM16.0273 9.38379C15.7345 9.09097 15.2597 9.09113 14.9668 9.38379L10.9971 13.3535L9.52734 11.8838C9.23452 11.591 8.75971 11.5911 8.4668 11.8838C8.1739 12.1767 8.1739 12.6514 8.4668 12.9443L10.4668 14.9443C10.7597 15.2371 11.2345 15.2372 11.5273 14.9443L16.0273 10.4443C16.32 10.1514 16.3201 9.67662 16.0273 9.38379Z' fill='black'/%3E %3C/svg%3E");
  --icon-task-drag: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E %3Cpath d='M9.25 16.5C10.2165 16.5 11 17.2835 11 18.25C11 19.2165 10.2165 20 9.25 20C8.2835 20 7.5 19.2165 7.5 18.25C7.5 17.2835 8.2835 16.5 9.25 16.5Z' fill='black'/%3E %3Cpath d='M14.75 16.5C15.7165 16.5 16.5 17.2835 16.5 18.25C16.5 19.2165 15.7165 20 14.75 20C13.7835 20 13 19.2165 13 18.25C13 17.2835 13.7835 16.5 14.75 16.5Z' fill='black'/%3E %3Cpath d='M9.25 10.1504C10.2165 10.1504 11 10.9339 11 11.9004V12C11 12.9665 10.2165 13.75 9.25 13.75C8.2835 13.75 7.5 12.9665 7.5 12V11.9004C7.5 10.9339 8.2835 10.1504 9.25 10.1504Z' fill='black'/%3E %3Cpath d='M14.75 10.1504C15.7165 10.1504 16.5 10.9339 16.5 11.9004V12C16.5 12.9665 15.7165 13.75 14.75 13.75C13.7835 13.75 13 12.9665 13 12V11.9004C13 10.9339 13.7835 10.1504 14.75 10.1504Z' fill='black'/%3E %3Cpath d='M9.25 4C10.2165 4 11 4.7835 11 5.75C11 6.7165 10.2165 7.5 9.25 7.5C8.2835 7.5 7.5 6.7165 7.5 5.75C7.5 4.7835 8.2835 4 9.25 4Z' fill='black'/%3E %3Cpath d='M14.75 4C15.7165 4 16.5 4.7835 16.5 5.75C16.5 6.7165 15.7165 7.5 14.75 7.5C13.7835 7.5 13 6.7165 13 5.75C13 4.7835 13.7835 4 14.75 4Z' fill='black'/%3E %3C/svg%3E");
}

/* Shared mask helper — applies to both .md-task-drag/.md-task-checkbox spans
   and the Milkdown editor ::before pseudo-element. */
.md-task-drag, .md-task-checkbox,
.detail-body-editor .ProseMirror li[data-item-type="task"]::before {
  display: inline-block;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  background-color: var(--text-primary);
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
}

/* ── Task list (checklist) — read path (marked.js + transformTaskItems) ── */

.detail-description ul:has(.md-task-item),
.detail-description .md-list--tasks {
  list-style: none;
  padding-left: 0;
}

.detail-description .md-task-item {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  padding: var(--sp-3xs) 0;
  margin-bottom: 0;
  line-height: 1.5;
}

.detail-description .md-task-drag {
  -webkit-mask-image: var(--icon-task-drag);
          mask-image: var(--icon-task-drag);
  margin-top: 2px;
  opacity: 0.4;
  cursor: grab;
}

.detail-description .md-task-checkbox {
  -webkit-mask-image: var(--icon-task-unchecked);
          mask-image: var(--icon-task-unchecked);
  margin-top: 2px;
}

.detail-description .md-task-item--checked .md-task-checkbox {
  -webkit-mask-image: var(--icon-task-checked);
          mask-image: var(--icon-task-checked);
}

.detail-description .md-task-item--checked {
  color: var(--text-tertiary);
  text-decoration: line-through;
}

.detail-description .md-task-item--checked .md-task-drag,
.detail-description .md-task-item--checked .md-task-checkbox {
  text-decoration: none;
  background-color: var(--text-tertiary);
}

/* ── Milkdown editor task list items (owner edit path) ─────────────────── */
/* Milkdown GFM: <li data-item-type="task" data-checked="true|false">.
   Icons are real DOM elements (.pm-task-drag, .pm-task-check) injected by
   a Decoration.widget plugin — ::before inside contenteditable doesn't
   receive pointer events reliably in WebKit. */

.detail-body-editor .ProseMirror ul:has(li[data-item-type="task"]) {
  list-style: none;
  padding-left: 0;
}

/* Layout: iOS Reminders anatomy — checkbox gutter on the LEFT, drag handle
   pinned to the row's RIGHT edge. Both buttons are absolutely positioned,
   NOT flow siblings of the text: inline contenteditable=false neighbors
   reproduce a known Chromium/WebKit bug where the native caret fails to
   render/blink next to them. Out-of-flow placement keeps the text run's
   inline layout free of non-editable neighbors on BOTH ends.
   `top: 0.75em - 8px` centers each 16px icon on the first text line
   (line-height 1.5), which reads as vertical centering on single-line
   rows and stays pinned to the first line when the text wraps. */
.detail-body-editor .ProseMirror li[data-item-type="task"] {
  position: relative;
  padding: var(--sp-3xs) 28px var(--sp-3xs) 24px;
  margin-bottom: 0;
  line-height: 1.5;
}

.detail-body-editor .ProseMirror .pm-task-check {
  position: absolute;
  left: 0;
  top: calc(var(--sp-3xs) + 0.75em - 8px);
  user-select: none;
}

.detail-body-editor .ProseMirror .pm-task-drag {
  position: absolute;
  right: 0;
  top: calc(var(--sp-3xs) + 0.75em - 8px);
  user-select: none;
}

/* Button reset — strip browser defaults so we get a clean 16×16 icon button */
.detail-body-editor .ProseMirror .pm-task-drag,
.detail-body-editor .ProseMirror .pm-task-check {
  appearance: none;
  -webkit-appearance: none;
  display: inline-block;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  border: none;
  background-color: var(--text-primary);
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  cursor: pointer;
  outline: none;
}

.detail-body-editor .ProseMirror .pm-task-drag {
  -webkit-mask-image: var(--icon-task-drag);
          mask-image: var(--icon-task-drag);
  cursor: grab;
  opacity: 0.5;
}

.detail-body-editor .ProseMirror .pm-task-check {
  -webkit-mask-image: var(--icon-task-unchecked);
          mask-image: var(--icon-task-unchecked);
}

.detail-body-editor .ProseMirror .pm-task-check--checked {
  -webkit-mask-image: var(--icon-task-checked);
          mask-image: var(--icon-task-checked);
}

.detail-body-editor .ProseMirror li[data-item-type="task"][data-checked="true"] {
  color: var(--text-tertiary);
  text-decoration: line-through;
  font-style: italic;
}

/* Visual feedback during pointer drag-to-reorder: the row itself rides
   under the pointer (inline translateY set by the NodeView) as a lifted
   card. pointer-events:none is load-bearing — trackDrop's elementFromPoint
   must see the rows BENEATH the lifted one. */
.detail-body-editor .ProseMirror li.task-dragging {
  backdrop-filter: blur(2px);
  pointer-events: none;
  position: relative;
  z-index: 5;
  background: rgba(var(--color-blue), .4);
  border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
  opacity: 0.95;
  will-change: transform;
}
.detail-body-editor .ProseMirror li.task-drop-above {
  border-top: 2px solid var(--accent-blue);
}
.detail-body-editor .ProseMirror li.task-drop-below {
  border-bottom: 2px solid var(--accent-blue);
}

.detail-description code {
  font-family: "SF Mono", "Fira Code", monospace;
  font-size: 0.9em;
  background: rgba(0, 0, 0, 0.06);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}
[data-theme="dark"] .detail-description code { background: rgba(255, 255, 255, 0.1); }
[data-theme="light"] .detail-description code { background: rgba(0, 0, 0, 0.06); }

.detail-description pre {
  background: rgba(0, 0, 0, 0.06);
  padding: var(--sp-sm);
  border-radius: var(--radius-md);
  overflow-x: auto;
  margin: var(--sp-xs) 0;
}
[data-theme="dark"] .detail-description pre { background: rgba(255, 255, 255, 0.08); }
[data-theme="light"] .detail-description pre { background: rgba(0, 0, 0, 0.06); }
.detail-description pre code { background: none; padding: 0; }

.detail-description blockquote {
  border-left: 3px solid var(--border-primary);
  padding-left: var(--sp-sm);
  color: var(--text-secondary);
  margin: var(--sp-xs) 0;
}

.detail-description strong { font-weight: 600; color: var(--text-primary); }
.detail-description em { font-style: italic; }

.detail-description a {
  color: var(--accent-blue);
  text-decoration: underline;
}

/* Owner-only mount point for the Milkdown body editor. Milkdown injects
 * .milkdown > .ProseMirror once initialised. Mirrors the chat composer's
 * styling pattern (chat.css §"Milkdown rich-text composer") but scaled
 * to detail-page typography so the editable surface visually matches
 * `.detail-description` (the non-owner read path). */
.detail-body-editor {
  min-height: 1.6em;
  cursor: text;
  outline: none;
}

.detail-body-editor .milkdown {
  all: unset;
  display: block;
  width: 100%;
}

.detail-body-editor .ProseMirror {
  outline: none;
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--text-primary);
  line-height: 1.6;
  margin: 0;
  padding: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

.detail-body-editor .ProseMirror p.is-editor-empty:first-child::before,
.detail-body-editor .ProseMirror:empty::before {
  content: "Add a description…";
  float: left;
  height: 0;
  color: var(--text-tertiary);
  pointer-events: none;
}

.detail-body-editor .ProseMirror p {
  margin: 0 0 var(--sp-xs) 0;
}
.detail-body-editor .ProseMirror p:last-child {
  margin-bottom: 0;
}

.detail-body-editor .ProseMirror strong { font-weight: 700; }
.detail-body-editor .ProseMirror em     { font-style: italic; }
.detail-body-editor .ProseMirror code {
  font-family: monospace;
  font-size: 0.9em;
  background: var(--bg-secondary, rgba(0,0,0,0.08));
  border-radius: 3px;
  padding: 0.1em 0.3em;
}
.detail-body-editor .ProseMirror a {
  color: var(--color-accent, #4a90d9);
  text-decoration: underline;
}

.detail-body-editor .ProseMirror ul,
.detail-body-editor .ProseMirror ol {
  margin: var(--sp-xs) 0;
  padding-left: 1.4em;
}
.detail-body-editor .ProseMirror ul { list-style-type: disc; }
.detail-body-editor .ProseMirror ol { list-style-type: decimal; }
.detail-body-editor .ProseMirror li { margin: 0; }

.detail-body-editor .ProseMirror pre {
  background: var(--bg-secondary, rgba(0,0,0,0.08));
  border-radius: var(--radius-sm, 4px);
  padding: var(--sp-xs) var(--sp-sm);
  overflow-x: auto;
  font-family: monospace;
  font-size: 0.9em;
  margin: var(--sp-xs) 0;
}
.detail-body-editor .ProseMirror pre code {
  background: none;
  padding: 0;
}

.detail-body-editor .ProseMirror blockquote {
  border-left: 3px solid var(--border-primary, #ccc);
  margin: var(--sp-xs) 0;
  padding-left: var(--sp-sm);
  color: var(--text-secondary);
}

.detail-body-editor .ProseMirror h1,
.detail-body-editor .ProseMirror h2,
.detail-body-editor .ProseMirror h3 {
  font-weight: 700;
  margin: var(--sp-sm) 0 var(--sp-xs) 0;
  line-height: 1.3;
  color: var(--text-primary);
}

.detail-body-editor .ProseMirror ::selection {
  background: var(--color-accent-alpha, rgba(74,144,217,0.25));
}

.detail-description hr {
  border: none;
  border-top: 1px solid var(--border-primary);
  margin: var(--sp-md) 0;
}

.detail-text-body {
  font-size: var(--text-base);
  color: var(--text-primary);
  line-height: 1.7;
  margin-top: var(--sp-xs);
}

.detail-text-body p {
  margin: 0 0 var(--sp-sm) 0;
}
.detail-text-body p:last-child {
  margin-bottom: 0;
}

.detail-text-body h1,
.detail-text-body h2,
.detail-text-body h3,
.detail-text-body h4 {
  font-weight: 600;
  margin: var(--sp-md) 0 var(--sp-xs);
}
.detail-text-body h1 { font-size: var(--text-2xl); }
.detail-text-body h2 { font-size: var(--text-xl); }
.detail-text-body h3 { font-size: var(--text-lg); }
.detail-text-body h4 { font-size: var(--text-base); }

.detail-text-body ul,
.detail-text-body ol {
  margin: var(--sp-xs) 0;
  padding-left: var(--sp-lg);
}
.detail-text-body ul { list-style: disc; }
.detail-text-body ol { list-style: decimal; }
.detail-text-body li { margin-bottom: var(--sp-3xs); }

.detail-text-body code {
  font-family: "SF Mono", "Fira Code", monospace;
  font-size: 0.9em;
  background: rgba(0, 0, 0, 0.06);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}
[data-theme="dark"] .detail-text-body code { background: rgba(255, 255, 255, 0.1); }
[data-theme="light"] .detail-text-body code { background: rgba(0, 0, 0, 0.06); }

.detail-text-body pre {
  background: rgba(0, 0, 0, 0.06);
  padding: var(--sp-sm);
  border-radius: var(--radius-md);
  overflow-x: auto;
  margin: var(--sp-xs) 0;
}
[data-theme="dark"] .detail-text-body pre { background: rgba(255, 255, 255, 0.08); }
[data-theme="light"] .detail-text-body pre { background: rgba(0, 0, 0, 0.06); }
.detail-text-body pre code { background: none; padding: 0; }

.detail-text-body blockquote {
  border-left: 3px solid var(--border-primary);
  padding-left: var(--sp-sm);
  color: var(--text-secondary);
  margin: var(--sp-xs) 0;
}

.detail-text-body strong { font-weight: 600; }
.detail-text-body em { font-style: italic; }

.detail-text-body a {
  color: var(--accent-blue);
  text-decoration: underline;
}

.detail-text-body hr {
  border: none;
  border-top: 1px solid var(--border-primary);
  margin: var(--sp-md) 0;
}

/* ── Extra text section (transcript / extracted page text) ──── */

.detail-extra-section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
}

.detail-extra-label {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.detail-extra-content {
  position: relative;
  max-height: 220px;
  overflow: hidden;
}

.detail-extra-content--expanded {
  max-height: none;
  overflow: visible;
}

.detail-extra-fade {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 80px;
  background: linear-gradient(to bottom, transparent, var(--bg-body));
  pointer-events: none;
}

.detail-extra-toggle {
  align-self: flex-start;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-tertiary);
  cursor: pointer;
}
.detail-extra-toggle:hover {
  color: var(--text-primary);
}

.detail-bookmark-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  margin-top: var(--sp-xs);
  color: var(--accent-blue);
  font-size: var(--text-sm);
  text-decoration: none;
}
.detail-bookmark-link:hover {
  text-decoration: underline;
}

.detail-actions {
  display: flex;
  gap: var(--sp-xs);
  flex-wrap: wrap;
  margin-top: var(--sp-xs);
}

.detail-action-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  white-space: nowrap;
}
.detail-action-btn:hover {
  border-color: var(--border-primary);
  background: var(--bg-subtle);
}

.detail-action-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-tertiary);
}

.detail-action-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.detail-action-label {
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 500;
}

/* Right-side vertical action rail */
.detail-rail {
  position: absolute;
  bottom: 44px;
  right: 44px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

.detail-rail-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var( --size-3xl);
  height: var( --size-3xl);
  padding: var(--sp-3xs);
  border: none;
  background: var(--bg-primary);
  border-radius: var(--radius-full);
  color: var(--text-primary);
  cursor: pointer;
  font-family: inherit;
}
.detail-rail-btn:hover {
  color: var(--text-primary);
  opacity: 0.7;
}

@media (min-width: 769px) {
  .detail-rail-btn:has(.detail-rail-count:not(:empty)) {
    padding-inline: var(--sp-xs);
  }
}

.detail-rail-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.detail-rail-count {
  font-size: 9px;
  line-height: 1;
  color: var(--text-secondary);
}

.detail-rail-icon .icon {
  width: 18px;
  height: 18px;
}

/* The like rail entry is a real <button>; reset the default chrome so it
   matches the surrounding rail_btn divs, and show the filled/colored
   state when the viewer has already liked the item. */
.detail-rail-btn-like {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  width: fit-content;
  border: none;
  font-family: inherit;
  gap: 4px;
}


.detail-rail-btn-like > .detail-rail-count {
  font-size: var(--size-sm);
}

.detail-rail-btn-like-active,
.detail-rail-btn-like-active:hover {
  color: var(--text-parimary);
}

.detail-rail-btn-like-active {
  padding-left: 8px;
  padding-right: 8px;
  transition: .3s ease all;
}

/* Bookmark rail button — same reset as the like button. */
.detail-rail-btn-bookmark {
  border: none;
  font-family: inherit;
}
.detail-rail-btn-bookmark-active,
.detail-rail-btn-bookmark-active:hover {
  color: var(--text-primary);
}
/* Visibility toggle — pill track with sliding thumb */
.detail-visibility-toggle {
  position: relative;
  width: 64px;
  height: var(--size-3xl);
  border-radius: var(--radius-full);
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transition: background 0.2s, border-color 0.2s;
}

.detail-visibility-toggle--on {
  background: var(--bg-body);
  border-color: var(--border-primary-inverse);
}

.detail-visibility-toggle-thumb {
  position: absolute;
  top: 3px;
  left: 2px;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  /* background: var(--bg-quarternary); */
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  transition: transform 0.2s, background 0.2s, color 0.2s;
}

.detail-visibility-toggle > .detail-visibility-toggle-thumb {
  background: var(--bg-secondary);
  color: var(--text-secondary);
}

.detail-visibility-toggle--on > .detail-visibility-toggle-thumb {
  background: var(--bg-primary-inverse);
  color: var(--text-primary-inverse);
}

.detail-visibility-toggle--on .detail-visibility-toggle-thumb {
  transform: translateX(24px);
}

/* Wrapper that stacks the toggle + groups-pill vertically in the rail */
.detail-visibility-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

/* "N People" pill shown when visibility is GROUPS */
.detail-groups-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-primary);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
}
.detail-groups-pill:hover {
  background: var(--bg-tertiary);
}

/* ─── Privacy options sheet ──────────────────────────────────────────────── */

.vis-sheet {
  height: auto;
  min-height: unset;
  max-height: 80vh;
}

.vis-sheet-content {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-bottom: var(--sp-md);
}

.vis-sheet-option {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  width: 100%;
  padding: var(--sp-sm) var(--sp-xs);
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  color: var(--text-primary);
  transition: background 0.15s;
}
.vis-sheet-option:hover {
  background: var(--bg-secondary);
}
.vis-sheet-option--active {
  background: var(--bg-secondary);
}

.vis-sheet-option-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.vis-sheet-option-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.vis-sheet-option-label {
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--text-primary);
}

.vis-sheet-option-sublabel {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.3;
}

.vis-sheet-option-check {
  display: flex;
  align-items: center;
  color: var(--text-primary);
  flex-shrink: 0;
}

/* ─── Groups picker sheet ────────────────────────────────────────────────── */

.groups-picker-sheet,
.groups-manage-sheet {
  height: 80vh;
  display: flex;
  flex-direction: column;
}

.groups-picker-header,
.groups-manage-header {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-sm);
  padding: var(--sp-sm) 0 var(--sp-md);
  flex-shrink: 0;
}

.groups-picker-header-icon,
.groups-manage-header-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  flex-shrink: 0;
  margin-top: 2px;
}

.groups-picker-header-text,
.groups-manage-header-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.groups-picker-title,
.groups-manage-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.groups-picker-subtitle,
.groups-manage-subtitle {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.35;
}

.groups-picker-close-btn {
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 4px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.groups-manage-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  padding: 4px 0;
  flex-shrink: 0;
  white-space: nowrap;
}

.groups-picker-search-wrap {
  padding-bottom: var(--sp-xs);
}

.groups-picker-search {
  width: 100%;
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-primary);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-size: var(--text-sm);
  outline: none;
}
.groups-picker-search:focus {
  border-color: var(--border-primary-inverse);
}

.groups-picker-count {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  padding: var(--sp-xs) 0 4px;
}

.groups-manage-label {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  padding-bottom: 4px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.groups-picker-list,
.groups-manage-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.groups-picker-empty {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  text-align: center;
  padding: var(--sp-lg) 0;
}

/* Shared row layout used in both picker and manage views */
.groups-picker-row {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-xs) 0;
}

.groups-picker-row-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
  min-width: 0;
}

.groups-picker-row-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.groups-picker-row-username {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.groups-picker-add-btn {
  border: 1px solid var(--border-primary);
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 4px 12px;
  border-radius: var(--radius-full);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s;
}
.groups-picker-add-btn:hover {
  background: var(--bg-secondary);
}

.groups-picker-remove-wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.groups-picker-remove-btn {
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  padding: 4px 2px;
}

.groups-picker-remove-x {
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  padding: 2px;
  display: flex;
  align-items: center;
}

.groups-picker-confirm-btn {
  position: sticky;
  bottom: 0;
  width: calc(100% + var(--sp-md) * 2);
  margin: 0 calc(-1 * var(--sp-md));
  padding: var(--sp-sm) var(--sp-md);
  background: var(--bg-primary);
  border: none;
  border-top: 1px solid var(--border-primary);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  text-align: center;
  transition: background 0.15s;
}
.groups-picker-confirm-btn:hover {
  background: var(--bg-secondary);
}

/* Chat-with-AI rail button — accent-colored to signal the primary action,
   dimmed while the Stream create-channel request is in flight. */
.detail-rail-btn-ai {
  color: var(--text-primary);
  background: var(--bg-primary);
}
.detail-rail-btn-ai:hover {
  /* color: var(--bg-secondary); */
  opacity: 0.8;
}
.detail-rail-btn-ai-loading {
  opacity: 0.5;
  cursor: progress;
}

/* Unread dot on the Chat-with-AI rail button. Item AI chats are hidden
   from the sidebar, so this dot is the only place users see new agent
   replies. Positioned over the top-right corner of the round button. */
.detail-rail-btn-ai {
  position: relative;
}
.detail-rail-btn-ai-unread-dot {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--accent-red);
  border: 2px solid var(--bg-primary);
  box-sizing: content-box;
  pointer-events: none;
}

/* Voice hero + player wrapper — player renders below the gradient hero. */
.detail-voice-wrapper {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

/* ── Audio player for voice detail ────────────────────────────── */

.detail-player {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  padding: var(--sp-md) var(--sp-lg);
  background: var(--bg-subtle);
  border-radius: var(--radius-lg);
}

.detail-player-btn {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  cursor: pointer;
  flex-shrink: 0;
}

.detail-player-progress { flex: 1; }

.detail-player-bar {
  height: 4px;
  background: var(--border-primary);
  border-radius: var(--radius-full);
  overflow: hidden;
  cursor: pointer;
}
.detail-player-bar-fill {
  height: 100%;
  background: var(--bg-secondary);
  transition: width 0.1s linear;
}

.detail-player-times {
  display: flex;
  justify-content: space-between;
  margin-top: var(--sp-3xs);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.detail-player-transcript {
  margin: var(--sp-sm) 0 0 0;
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.8);
  line-height: 1.5;
}

.detail-loading,
.detail-error {
  padding: var(--sp-xxl);
  text-align: center;
  color: var(--text-tertiary);
}

/* ── Container attachment grid ──────────────────────────────────── */

.detail-container-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--sp-sm);
}

.detail-container-thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-md, 12px);
  background: var(--surface-secondary, rgba(127, 127, 127, 0.08));
  display: block;
}

.detail-container-attach {
  margin-top: var(--sp-sm);
}

.detail-container-attach-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-md);
  border-radius: var(--radius-md, 12px);
  border: 1px dashed var(--border-subtle, rgba(127, 127, 127, 0.35));
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  cursor: pointer;
}

.detail-container-attach-btn:hover {
  background: var(--surface-secondary, rgba(127, 127, 127, 0.08));
  color: var(--text-primary);
}

.detail-container-file {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--sp-sm);
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md, 12px);
  background: var(--surface-secondary, rgba(127, 127, 127, 0.08));
  font-size: var(--text-xs);
  color: var(--text-secondary);
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* ── Inline Please AI chat (replaces comments section) ──────────── */

.detail-ai-chat {
  margin-top: var(--sp-lg);
  padding-bottom: var(--sp-4xl);
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

/* Owner reminder surface (.nr-root): inset the AI-chat section like the
   rest of the page's content (it previously ran flush on desktop), but
   leave it unboxed — it should blend into the plain page background
   rather than read as its own colored panel like the reminder card. */
.nr-root .detail-ai-chat {
  padding: var(--sp-lg) var(--sp-xl);
  background: var(--bg-secondary);
}

.detail-ai-chat-title {
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.detail-ai-chat-status {
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: var(--sp-md) 0;
}

/* Offline: Please AI is entirely server-side, so it can't be used at all
   without a connection. Framed as a state to wait out rather than as an
   error — the old copy ("Couldn't open the chat.") read like the feature
   was broken and gave no hint that reconnecting would fix it. */
.detail-ai-chat-offline {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-sm) var(--sp-md);
  margin-bottom: var(--sp-sm);

  border-radius: var(--radius-md, 10px);
  background: var(--fill-elevated-secondary);
  color: var(--text-secondary);
}

.detail-ai-chat-offline-text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 18px;
}

/* ── Camera capture modal (owner-container hardware camera) ───────── */

.camera-modal-body {
  display: flex;
  flex-direction: column;
  background: #000;
  border-radius: inherit;
  overflow: hidden;
}

.camera-viewfinder {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  background: #111;
}

.camera-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-lg) var(--sp-md);
  background: #000;
}

.camera-shutter-btn {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  border: 4px solid #fff;
  background: rgba(255, 255, 255, 0.85);
  cursor: pointer;
  transition: transform 80ms ease, background 80ms ease;
  flex-shrink: 0;
}

.camera-shutter-btn:hover {
  background: #fff;
  transform: scale(1.06);
}

.camera-shutter-btn:active {
  transform: scale(0.94);
  background: rgba(255, 255, 255, 0.65);
}

.detail-ai-chat-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

/* No independent scrollbox here — messages flow in the same single
   page-level scroll as the reminder card above (.nr-body owns the only
   overflow-y: auto on this page). */
.detail-ai-chat-messages {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

.detail-ai-chat-empty {
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: var(--sp-lg) 0;
  text-align: center;
}

.detail-ai-chat-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
}

/* Plain doc-like thread, not two-sided chat bubbles: the agent's messages
   (.detail-ai-msg-them) render as bare full-width text with a name+time
   header; only the viewer's own messages (.detail-ai-msg-me) get a bubble,
   right-aligned and width-capped like a normal chat reply. */
.detail-ai-msg {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.detail-ai-msg-them {
  align-self: stretch;
  max-width: 100%;
}

.detail-ai-msg-me {
  align-self: flex-end;
  align-items: flex-end;
  max-width: 85%;
}

.detail-ai-msg-header {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.detail-ai-msg-header-name {
  font-weight: 600;
  color: var(--text-primary);
}

.detail-ai-msg-bubble {
  padding: var(--sp-sm) var(--sp-md);
  border-radius: var(--radius-lg, 16px);
  background: var(--surface-secondary, rgba(127, 127, 127, 0.08));
}

.detail-ai-msg-me .detail-ai-msg-bubble {
  border-bottom-right-radius: var(--radius-sm);
  background: var(--fill-elevated-primary);
  color: var(--text-primary);
  backdrop-filter: contrast(2);
}

.detail-ai-msg-body {
  font-size: var(--text-sm);
  line-height: 1.5;
  overflow-wrap: break-word;
}

.detail-ai-msg-body p {
  margin: 0;
}

.detail-ai-msg-body p + p {
  margin-top: var(--sp-sm);
}

/* Floats directly above the composer's input pill (see ai_chat.gleam's
   view_composer — it's the first child inside .chat-composer, right
   before the form), the same spot chat.gleam's own typing indicator
   occupies. .chat-composer has no padding of its own in this detail
   context (.detail-ai-chat .chat-composer resets it to 0), so this
   supplies its own horizontal inset + the gap above the pill. */
.detail-ai-chat-typing {
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: 0 var(--sp-md);
  margin-bottom: var(--sp-sm);
}

.detail-ai-chat-typing-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-tertiary);
  animation: detail-ai-typing 1.2s infinite ease-in-out;
}

.detail-ai-chat-typing-dot:nth-child(2) { animation-delay: 0.15s; }
.detail-ai-chat-typing-dot:nth-child(3) { animation-delay: 0.3s; }

@keyframes detail-ai-typing {
  0%, 60%, 100% { opacity: 0.3; }
  30% { opacity: 1; }
}

.detail-ai-chat-composer {
  display: flex;
  gap: var(--sp-sm);
  align-items: flex-end;
}

.detail-ai-chat-input {
  flex: 1;
  resize: none;
  border: 1px solid var(--border-subtle, rgba(127, 127, 127, 0.2));
  border-radius: var(--radius-md, 12px);
  padding: var(--sp-sm) var(--sp-md);
  font: inherit;
  font-size: var(--text-sm);
  background: var(--bg-input, transparent);
  color: var(--text-primary);
  min-height: 40px;
  max-height: 160px;
}

.detail-ai-chat-input::placeholder {
  color: var(--text-tertiary);
}

.detail-ai-chat-attach {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: var(--radius-md, 12px);
  border: 1px solid var(--border-subtle, rgba(127, 127, 127, 0.2));
  background: var(--bg-input, transparent);
  color: var(--text-secondary);
  font-size: var(--text-lg);
  cursor: pointer;
}

@media (hover: hover) {
  .detail-ai-chat-attach:hover {
    background: var(--surface-secondary, rgba(127, 127, 127, 0.08));
  }
}

.detail-ai-attach-input {
  display: none;
}

.detail-ai-chat-send {
  border: none;
  border-radius: var(--radius-md, 12px);
  padding: 0 var(--sp-lg);
  height: 40px;
  background: var(--accent-blue);
  color: #fff;
  font-weight: 600;
  cursor: pointer;
}

.detail-ai-chat-send:disabled {
  background: var(--bg-disabled);
  color: var(--text-disabled);
  cursor: not-allowed;
}

/* ── Transparent backdrop for closing dropdowns on outside click ── */

.detail-backdrop {
  position: fixed;
  inset: 0;
  z-index: 9;
}

/* ── Reminder picker dropdown ────────────────────────────────── */

.detail-action-remind-wrapper {
  position: relative;
}

.detail-reminder-picker {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 10;
  margin-top: var(--sp-xs);
  padding: var(--sp-xs);
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
  min-width: 220px;
}

/* When the reminder picker is rendered inside the owner-container slide-up
   sheet (Ping Me), drop the absolute dropdown positioning and let it sit
   in-flow, full-width, without the border/shadow the sheet already provides. */
.nr-reminder-sheet .detail-reminder-picker {
  position: static;
  margin-top: 0;
  padding: 0;
  border: none;
  box-shadow: none;
  min-width: 0;
  width: 100%;
}

.detail-reminder-option {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  width: 100%;
  padding: var(--sp-xs) var(--sp-sm);
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: inherit;
  text-align: left;
  border-radius: var(--radius-md);
  cursor: pointer;
}
.detail-reminder-option:hover {
  background: var(--bg-subtle);
}
.detail-reminder-option-time {
  margin-left: auto;
  color: var(--text-tertiary);
  font-size: var(--text-xs);
}

.detail-reminder-custom {
  display: flex;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-sm);
  border-top: 1px solid var(--border-primary);
  margin-top: var(--sp-3xs);
  padding-top: var(--sp-sm);
}

.detail-reminder-custom-input {
  flex: 1;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  padding: var(--sp-3xs) var(--sp-xs);
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  background: var(--bg);
}

.detail-reminder-custom-btn {
  padding: var(--sp-3xs) var(--sp-sm);
  border: none;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-family: inherit;
  cursor: pointer;
}
.detail-reminder-custom-btn:hover {
  opacity: 0.9;
}

/* ── Existing reminders (chips below action row) ─────────────── */

.detail-reminders-list {
  display: flex;
  flex-direction: row;
  gap: 16px;
  margin-top: var(--sp-sm);
  justify-content: space-between;
  align-items: center;
}

.detail-view--actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-sm);
  flex: 1 0 auto;
}

.detail-reminder-chip-wrap {
  position: relative;
  display: inline-flex;
}

.detail-reminder-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  height: var(--size-3xl);
  padding: var(--sp-3xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  background: var(--bg-subtle);
  font-size: var(--text-sm);
  font-family: inherit;
  color: var(--text-primary);
  cursor: pointer;
}
.detail-reminder-chip:hover {
  background: var(--bg-hover, var(--bg-subtle));
  border-color: var(--border-primary);
}

.detail-reminder-chip-icon {
  display: inline-flex;
  align-items: center;
  color: var(--text-tertiary);
  line-height: 1;
}

.detail-reminder-chip-date {
  color: var(--text-primary);
}

.detail-reminder-chip-overdue {
  border-color: var(--accent-red);
  color: var(--accent-red);
}
.detail-reminder-chip-overdue .detail-reminder-chip-icon,
.detail-reminder-chip-overdue .detail-reminder-chip-date {
  color: var(--accent-red);
}

/* Chip action dropdown */
.detail-reminder-chip-menu {
  position: absolute;
  top: calc(100% + var(--sp-3xs));
  left: 0;
  z-index: 20;
  min-width: 140px;
  padding: var(--sp-3xs);
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.detail-reminder-chip-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  width: 100%;
  padding: var(--sp-xs) var(--sp-sm);
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: inherit;
  text-align: left;
  border-radius: var(--radius-md);
  cursor: pointer;
  white-space: nowrap;
}
.detail-reminder-chip-menu-item:hover {
  background: var(--bg-subtle);
}
.detail-reminder-chip-menu-item-danger {
  color: var(--accent-red);
}
.detail-reminder-chip-menu-item-danger:hover {
  background: var(--danger-subtle, var(--bg-subtle));
}

/* ── Keyboard-nav end-of-list toast ──────────────────────────────────── */

.detail-nav-toast {
  position: absolute;
  bottom: var(--sp-xl);
  left: 50%;
  transform: translateX(-50%);
  padding: var(--sp-xs) var(--sp-md);
  background: var(--bg-elevated, var(--bg-primary));
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  white-space: nowrap;
  pointer-events: none;
  z-index: 10;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  animation: detail-toast-in 0.18s ease-out, detail-toast-out 0.3s ease-in 2.2s forwards;
}

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes detail-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

@keyframes detail-toast-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (max-width: 768px) {
  .detail-content {
    width: 100%;
    max-width: 100%;
  }

  .detail-back { display: none; }
  .detail-header-sidebar-toggle { display: inline-flex; }

  /* 16px stops iOS Safari from zooming the composer on focus. */
  .detail-ai-chat-input {
    font-size: 16px;
  }

  /* Remove scroll-container horizontal padding so media bleeds edge-to-edge,
     then re-apply the same value directly to content sections. */
  .detail-scroll {
    padding-left: 0;
    padding-right: 0;
  }

  .detail-meta,
  .detail-text-content,
  .detail-ai-chat {
    padding-left: var(--sp-xl);
    padding-right: var(--sp-xl);
  }

  /* Full-width media on mobile — let natural aspect ratio dictate height. */
  .detail-hero {
    border-radius: 0;
  }

  .detail-hero-photo,
  .detail-hero-voice,
  .detail-hero-bookmark,
  .detail-hero-text {
    width: 100%;
    height: auto;
    aspect-ratio: unset;
    max-width: 100%;
    border-radius: 0;
  }

  .detail-hero-img {
    width: 100%;
    height: auto;
    object-fit: contain;
    border-radius: 0;
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .detail-description code { background: rgba(255, 255, 255, 0.1); }
  :root:not([data-theme]) .detail-description pre { background: rgba(255, 255, 255, 0.08); }
  :root:not([data-theme]) .detail-text-body code { background: rgba(255, 255, 255, 0.1); }
  :root:not([data-theme]) .detail-text-body pre { background: rgba(255, 255, 255, 0.08); }
}

.detail-no-access {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
  padding: var(--sp-xl);
  color: var(--text-tertiary);
  font-size: var(--text-lg);
  text-align: center;
}

/* ── Title-row attachment thumbnail deck ─────────────────────────────────
   Port of iOS CaptureThumbnailStack: up to three tilted 80×80 cards inside
   a 96×88 footprint, trailing the container title. One click target. */

.detail-title-row--with-thumb {
  flex-wrap: nowrap;
  align-items: flex-start;
}

.detail-title-row--with-thumb .detail-title {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
}

.thumb-stack {
  position: relative;
  flex: 0 0 auto;
  align-self: flex-start;
  /* Tight 80px box — the fan/rotation is transform-only and overhangs
     the box visibly without costing any layout height. */
  width: 80px;
  height: 80px;
  margin-left: 6px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.thumb-stack-card {
  position: absolute;
  top: 0;
  left: 0;
  width: 80px;
  height: 80px;
  box-sizing: border-box;
  border: 2px solid var(--bg-body); /* base background — flips with theme */
  border-radius: 16px;
  background: var(--bg-body);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
}

.thumb-stack-front {
  transform: rotate(-7deg);
  z-index: 3;
}

.thumb-stack-back1 {
  transform: translate(6px, 2px) rotate(6deg);
  opacity: 0.9;
  z-index: 2;
}

.thumb-stack-back2 {
  transform: translate(-5px, 2px) rotate(-3deg);
  opacity: 0.9;
  z-index: 1;
}

/* Inner clip so media cover-crops to the rounded card without clipping the
   +N badge (mirrors iOS, where the overlay draws after clipShape). */
.thumb-stack-clip {
  position: absolute;
  inset: 0;
  border-radius: 14px; /* card radius minus border width */
  overflow: hidden;
}

.thumb-stack-media {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.thumb-stack-placeholder {
  background: var(--bg-secondary);
}

/* Calendar date badge card — scaled-up sibling of the 56px timeline badge
   (.tl-thumb-calendar): red uppercase month over the day numeral. */
.thumb-stack-calendar {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  background: var(--fill-elevated-quaternary);
}

.thumb-stack-cal-month {
  font-size: 14px;
  font-weight: 600;
  line-height: 17px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent-red);
}

.thumb-stack-cal-day {
  font-size: 32px;
  font-weight: 500;
  line-height: 36px;
  color: var(--tl-text-primary);
}

.thumb-stack-voice {
  position: relative; /* anchors the play chip */
  display: flex;
  align-items: center;
  justify-content: center;
}

.thumb-stack-voice .wf-tile {
  height: 100%;
  border-radius: 0; /* the card's clip owns the rounding */
}

/* Translucent play chip centered on the voice card — signals that tapping
   opens the fullscreen voice viewer. */
.thumb-stack-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.thumb-stack-badge {
  position: absolute;
  right: 4px;
  bottom: 4px;
  z-index: 1;
  padding: 2px 6px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
}

/* ============================================================
   Inline "Audio transcript:" block — standalone voice notes on the
   unified owner view (between the heading container and the body editor).
   ============================================================ */

.detail-audio-transcript {
  padding: var(--sp-sm) var(--sp-xl) 0;
}

.detail-audio-transcript-label {
  margin: 0 0 var(--sp-3xs);
  font-size: 13px;
  font-weight: 500;
  color: var(--text-tertiary);
}

.detail-audio-transcript-text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-secondary);
  white-space: pre-wrap;
}

/* ============================================================
   Fullscreen media viewer (voice notes + videos) — web port of iOS's
   VoiceNoteViewer / MediaViewer. Forced-dark surface like the image
   lightbox (.mo-lightbox); sits above the modal system (900), below
   the chat lightbox (1000).
   ============================================================ */

.media-viewer {
  position: fixed;
  inset: 0;
  z-index: 950;
  display: flex;
  flex-direction: column;
  background: #0b0b0d;
  color: #fff;
  animation: mo-lb-in 0.3s ease-in-out;
}

/* ── top bar ── */

.media-viewer-top {
  display: flex;
  align-items: center;
  padding: 12px 8px 8px;
  flex-shrink: 0;
}

.media-viewer-top-spacer { flex: 1; }

/* Uploaded/captured file name + extension, centered between the flex
   spacers (the ghost below balances a hidden ⋯ side). */
.media-viewer-title {
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-sm);
  font-weight: 500;
  color: rgba(255, 255, 255, 0.85);
}

/* Same footprint as an icon button — keeps the title centered when the
   right side has no ⋯ menu (local previews). */
.media-viewer-top-ghost {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
}

.media-viewer-icon-btn {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-full);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease;
}
.media-viewer-icon-btn:hover { background: rgba(255, 255, 255, 0.08); }

/* ── pager (dots / chevrons / image page) ── */

.media-viewer-image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* Animated page container: the keyed child remounts on page change, so
   the slide-in replays; direction class picks the entry side. */
.media-viewer-pagewrap {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.media-viewer-page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.media-viewer-pagewrap--fwd > .media-viewer-page {
  animation: mv-page-fwd 240ms cubic-bezier(0.25, 0.1, 0.25, 1);
}
.media-viewer-pagewrap--back > .media-viewer-page {
  animation: mv-page-back 240ms cubic-bezier(0.25, 0.1, 0.25, 1);
}
@keyframes mv-page-fwd {
  from {
    transform: translateX(48px);
    opacity: 0.3;
  }
  to {
    transform: none;
    opacity: 1;
  }
}
@keyframes mv-page-back {
  from {
    transform: translateX(-48px);
    opacity: 0.3;
  }
  to {
    transform: none;
    opacity: 1;
  }
}

/* Bottom pagination dots (iOS MediaViewer parity). */
.media-viewer-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
  padding: var(--sp-md) 0;
  flex-shrink: 0;
}
.media-viewer-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  border: none;
  padding: 0;
  background: rgba(255, 255, 255, 0.35);
  cursor: pointer;
}
.media-viewer-dot--current {
  background: #ffffff;
}

/* Desktop-only hover chevrons at the viewer edges. */
.media-viewer-chevrons {
  display: none;
}
@media (min-width: 769px) {
  .media-viewer-chevrons {
    display: block;
  }
  .media-viewer-chevron {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-full);
    background: rgba(118, 118, 128, 0.28);
    color: #ffffff;
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease;
    z-index: 2;
  }
  .media-viewer:hover .media-viewer-chevron {
    opacity: 1;
  }
  .media-viewer-chevron--prev { left: var(--sp-md); }
  .media-viewer-chevron--next { right: var(--sp-md); }
}

/* Destructive ⋯ row (Delete attachment). */
.media-viewer-menu-row--danger {
  color: var(--accent-red);
}

/* Delete-attachment confirm layer above the viewer. */
.media-viewer-confirm-layer {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-viewer-confirm-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}
.media-viewer-confirm {
  position: relative;
  width: min(360px, calc(100% - 2 * var(--sp-xl)));
  padding: var(--sp-lg);
  border-radius: var(--radius-lg);
  background: #2c2c2e;
  color: #ffffff;
}
.media-viewer-confirm-text {
  margin: 0 0 var(--sp-md);
  font-size: var(--text-base);
}
.media-viewer-confirm-actions {
  display: flex;
  gap: var(--sp-sm);
  justify-content: flex-end;
}
.media-viewer-confirm-btn {
  padding: var(--sp-xs) var(--sp-lg);
  border: none;
  border-radius: var(--radius-full);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  background: rgba(118, 118, 128, 0.24);
  color: #ffffff;
}
.media-viewer-confirm-btn--danger {
  background: var(--accent-red);
}

/* ── centered media ── */

.media-viewer-center {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 0 16px;
}

.media-viewer-canvas {
  width: min(320px, 78vw);
  height: 64px;
  cursor: pointer;
  touch-action: none; /* pointer-capture drag scrubbing */
}

.media-viewer-video {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--radius-sm);
}

/* ── playback bar + transcript ── */

.media-viewer-lower {
  flex-shrink: 0;
  width: min(560px, 100%);
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.media-viewer-playbar {
  display: flex;
  align-items: center;
  gap: 12px;
}

.media-viewer-play {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: #fff;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
}

.media-viewer-progress {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}

.media-viewer-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: #fff;
  transition: width 0.1s linear;
}

.media-viewer-time {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, 0.6);
  flex-shrink: 0;
}

.media-viewer-transcript {
  max-height: 32vh;
  overflow-y: auto;
}

.media-viewer-transcript-label {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.5);
}

.media-viewer-transcript-text {
  margin: 0;
  font-size: 17px;
  line-height: 1.45;
}

.media-viewer-seg {
  color: rgba(255, 255, 255, 0.45);
  transition: color 120ms ease;
}

.media-viewer-seg--current {
  color: #fff;
  font-weight: 600;
}

/* ── bottom action row (voice) ── */

.media-viewer-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 20px 8px 32px;
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.6);
}

/* ── ellipsis menu drawer ── */

.media-viewer-menu-layer {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.media-viewer-menu-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  cursor: pointer;
}

.media-viewer-menu {
  position: relative;
  background: #1c1c1f;
  border-radius: 24px 24px 0 0;
  padding: 8px 8px calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
}

.media-viewer-menu-handle {
  width: 36px;
  height: 5px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.25);
  margin: 4px auto 12px;
}

.media-viewer-menu-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
.media-viewer-menu-row:hover { background: rgba(255, 255, 255, 0.06); }

/* ── Inline AI chat composer (Messages-parity pill) ─────────────────────
   The composer reuses chat.css's .chat-composer* classes wholesale; these
   rules only re-scope chat-pane layout assumptions to the detail column. */

/* Chat pane gives .chat-composer grid placement + gutter padding — the
   detail column provides its own spacing. */
.detail-ai-chat .chat-composer {
  padding: 0;
  max-width: none;
}

/* Desktop-only: mobile instead gets the docked Please AI composer bar in
   the toolbar (ai_chat.view_composer_bar / .ai-composer-dock — see
   new_reminder.css's responsive-split section). This selector is scoped to
   .detail-ai-chat specifically, so it doesn't touch the Messages pane's own
   .chat-composer on mobile. */
@media (max-width: 768px) {
  .detail-ai-chat .chat-composer {
    display: none;
  }
}

/* Desktop composer stays nested in-flow inside .detail-ai-chat, right
   below the messages list — same section as the reminder's checklist
   above it, not pinned/floating. (A fixed-to-viewport version of this was
   tried and reverted — parking that idea for later.) */

/* No pill overrides: the AI-chat section's --bg-secondary surface gives
   the --bg-primary pill its contrast, and the component keeps chat.css's
   exact chrome (including the 2px transparent → colored focus border) so
   it looks and feels identical to the Messages composer. */

/* Generic (non image/video) staged file chip inside the preview strip. */
.detail-ai-composer-file {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3xs);
  width: 80px;
  height: 80px;
  padding: var(--sp-xs);
  color: var(--text-secondary);
}

.detail-ai-composer-file-name {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
}

/* chat.css hardcodes the post-mount empty-state placeholder to
   "Type a message..." — keep the AI composer's own prompt (the pre-mount
   ::before already reads data-placeholder). */
.detail-ai-chat .chat-composer-milkdown .ProseMirror p.is-editor-empty:first-child::before,
.detail-ai-chat .chat-composer-milkdown .ProseMirror:empty::before {
  content: "Message Please…";
}

/* Public (unauthenticated) item page — /item/<id> for anonymous visitors.
   The content column reuses the signed-in detail page's .detail-* classes
   (see detail.css); this file only supplies the page wrapper and the
   sign-in footer link. */

.public-item-page {
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--bg-primary);
}

.public-item-page .detail-scroll {
  flex: 1;
  padding-top: var(--sp-xl);
}

.public-item-signin {
  padding: var(--sp-md);
  text-align: center;
  font-weight: 600;
  text-decoration: none;
  color: var(--accent-blue);
  border-top: 1px solid var(--border-primary);
}

/* Capture modal — overlay + dialog for the New Capture flow. */

.capture-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.capture-modal {
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  width: min(440px, calc(100vw - 48px));
  min-height: 480px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: var(--sp-md);
  gap: var(--sp-xl);
}

.capture-modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
}

/* Header ------------------------------------------------------------------ */
.capture-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.capture-title {
  font-size: 16px;
  font-weight: 400;
  color: var(--text-primary);
  margin: 0;
}

.capture-close {
  background: transparent;
  border: none;
  color: var(--text-primary);
  cursor: pointer;
  padding: 0;
  width: 24px;
  height: 24px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.capture-close:hover { background: var(--bg-secondary); }
.capture-close .icon { width: 14px; height: 14px; }

/* Drop zone --------------------------------------------------------------- */
.capture-dropzone {
  position: relative; /* contains the absolutely-positioned file input */
  background: rgba(0, 0, 0, 0.04); /* --bg-quaternary not available in tokens */
  border-radius: var(--radius-md);
  padding: 15px var(--sp-xl);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.capture-dropzone-icon {
  display: flex;
  align-items: center;
  color: var(--text-primary);
}

.capture-dropzone-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

/* Nested inside its parent label and sized to cover it fully so the user
   is physically tapping the file input itself — bypasses iOS Safari's
   label-for activation quirks entirely. The label needs position:relative. */
.capture-file-input-hidden {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  /* font-size: 100px pushes the "Choose File" text off-screen on some
     Android browsers that render it inside the input bounds */
  font-size: 100px;
}

.capture-dropzone-text {
  font-size: 14px;
  font-weight: 400;
  color: var(--text-primary);
  margin: 0;
  text-align: center;
}
.capture-dropzone-link {
  color: var(--accent-blue);
  text-decoration: none;
  cursor: pointer;
}
.capture-dropzone-link:hover {
  text-decoration: underline;
}
.capture-dropzone-formats {
  font-size: 12px;
  color: var(--text-secondary);
  margin: var(--sp-3xs) 0 0;
}

/* Uploads list ------------------------------------------------------------ */
.capture-uploads {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}

.capture-upload {
  display: flex;
  gap: 12px;
  padding: 12px 14px;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 12px;
  align-items: flex-start;
}

.capture-upload-type-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  flex-shrink: 0;
  padding-top: 1px;
}

.capture-upload-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.capture-upload-header {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 8px;
  align-items: center;
}

.capture-upload-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: #ffffff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.capture-upload-remove {
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.5);
  padding: 0;
  width: 18px;
  height: 18px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.capture-upload-remove:hover { color: #ffffff; }

.capture-upload-percent {
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.5);
  font-variant-numeric: tabular-nums;
  min-width: 36px;
  text-align: right;
  flex-shrink: 0;
}
.capture-upload-percent--done {
  color: #22c55e;
}

.capture-upload-bar {
  height: 3px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.capture-upload-bar-fill {
  height: 100%;
  background: var(--accent-blue);
  transition: width 0.15s ease;
}
.capture-upload-bar-fill--done {
  background: #22c55e;
}

/* Image preview shown in drop mode after a file is selected */
.capture-drop-preview {
  position: relative;
  width: 100%;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-secondary);
  max-height: 240px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.capture-drop-preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  max-height: 240px;
  display: block;
}
.capture-drop-preview-change {
  position: absolute;
  bottom: var(--sp-xs);
  right: var(--sp-xs);
  padding: 4px 10px;
  font-size: var(--text-xs);
  font-weight: 500;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border-radius: var(--radius-full);
  cursor: pointer;
  user-select: none;
  transition: background 0.15s ease;
}
.capture-drop-preview-change:hover {
  background: rgba(0, 0, 0, 0.75);
}


/* Nav tabs (mode switches) ------------------------------------------------ */
.capture-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-xs);
}

.capture-nav-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text-tertiary);
  margin: 0;
}

.capture-nav-tabs {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 4px 0;
  gap: var(--sp-lg);
}

.capture-nav-tab {
  background: transparent;
  border: none;
  font: inherit;
  font-size: 16px;
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-full);
  transition: color 0.12s, background 0.12s;
}
.capture-nav-tab:hover {
  color: var(--text-primary);
}
.capture-nav-tab--active {
  font-weight: 600;
  color: var(--text-primary);
  background: var(--bg-secondary);
}

/* Footer ------------------------------------------------------------------ */
.capture-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-sm);
  padding-top: var(--sp-sm);
  border-top: 1px solid var(--border-primary);
}

.capture-btn {
  padding: var(--sp-xs) var(--sp-lg);
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border-primary);
  font-family: inherit;
}
.capture-btn-cancel {
  background: var(--bg-primary);
  color: var(--text-primary);
}
.capture-btn-cancel:hover {
  background: var(--bg-subtle);
}
.capture-btn-save {
  background: var(--bg-body);
  color: var(--text-primary);
  border-color: var(--border-primary);
}
.capture-btn-save:hover:not(:disabled) {
  background: var(--bg-subtle);
}
.capture-btn-save-disabled {
  background: var(--bg-subtle);
  color: var(--text-secondary);
  border-color: var(--border-primary);
  cursor: not-allowed;
}


/* Mode forms -------------------------------------------------------------- */
.capture-link-form,
.capture-text-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}

.capture-voice-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}

.capture-camera-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  align-items: center;
}

/* Desktop webcam viewfinder + shutter ------------------------------------ */
.capture-webcam-wrap {
  width: 100%;
  position: relative; /* needed for the absolute-positioned flip button */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-md);
}

.capture-webcam-viewfinder {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  background: #000;
  display: block;
  object-fit: cover;
}

.capture-webcam-shutter {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 3px solid var(--border-primary);
  background: var(--bg-primary);
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  transition: border-color 0.12s;
}
.capture-webcam-shutter::after {
  content: "";
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: var(--bg-disabled);
  transition: background 0.12s;
}
.capture-webcam-shutter--active::after {
  background: var(--accent-red);
}
.capture-webcam-shutter--active:hover::after {
  opacity: 0.8;
}
.capture-webcam-shutter:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

/* Camera flip button (desktop — overlaid bottom-right of webcam-wrap) ------- */
.capture-webcam-flip {
  position: absolute;
  bottom: var(--sp-md);
  right: var(--sp-md);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.12s;
  flex-shrink: 0;
}
.capture-webcam-flip:hover { background: rgba(0, 0, 0, 0.65); }
.capture-webcam-flip .icon { color: #fff; }

/* Webcam photo preview + retake ------------------------------------------ */
.capture-webcam-preview {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-md);
}

.capture-webcam-preview-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-md);
  object-fit: cover;
  display: block;
}

.capture-webcam-retake {
  background: transparent;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--sp-xs) var(--sp-lg);
  cursor: pointer;
}
.capture-webcam-retake:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
}

.capture-link-form label,
.capture-text-form label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.capture-link-form input,
.capture-text-form input,
.capture-text-content {
  padding: var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font: inherit;
  outline: none;
}
.capture-link-form input:focus,
.capture-text-form input:focus,
.capture-text-content:focus {
  border-color: var(--bg-secondary);
}

.capture-text-content {
  resize: vertical;
  min-height: 120px;
}

.capture-submit {
  padding: var(--sp-sm);
  border: none;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}
.capture-submit:disabled { opacity: 0.4; cursor: not-allowed; }

/* Voice ------------------------------------------------------------------- */

/* Override the shared form layout for voice — needs a tighter gap */
.capture-voice-form {
  gap: var(--sp-sm);
  align-items: center;
}

/* Waveform canvas — fills available width with a fixed height */
.capture-voice-waveform {
  width: 100%;
  height: 160px;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  display: block;
  cursor: pointer; /* scrub hint */
}

/* Taller canvas for the full-height mobile sheet */
.capture-voice-waveform--sheet {
  height: 200px;
}

/* Recording timer */
.capture-voice-timer {
  font-size: var(--text-xl);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.03em;
  color: var(--text-primary);
}

/* Controls row — play + record buttons side by side */
.capture-voice-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
}

/* Round icon button for play/pause */
.capture-voice-play-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1.5px solid var(--border-primary);
  background: var(--bg-primary);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.capture-voice-play-btn:hover {
  background: var(--bg-secondary);
}

/* Big pill record / stop / re-record button */
.capture-voice-record-btn {
  padding: 12px 28px;
  border: none;
  border-radius: var(--radius-full);
  background: #ef4444;
  color: #ffffff;
  font: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
}
.capture-voice-record-btn:hover {
  background: #dc2626;
}
/* Pulsing glow while actively recording */
.capture-voice-record-btn--recording {
  animation: voice-record-pulse 1.4s ease-in-out infinite;
}

@keyframes voice-record-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
  50%       { box-shadow: 0 0 0 8px rgba(239, 68, 68, 0); }
}

.capture-voice-hint,
.capture-camera-hint {
  text-align: center;
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

/* Camera file input ------------------------------------------------------- */
.capture-camera-input {
  display: block;
  margin: var(--sp-md) auto;
}

/* Sidebar button variant — used by "+ New Capture" row. ------------------- */
.sidebar-row-button {
  background: transparent;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  width: 100%;
  text-align: left;
}

/* Link preview card ------------------------------------------------------- */
.capture-link-preview {
  display: flex;
  gap: var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-secondary);
  animation: fadeIn 0.15s ease;
}

.capture-link-preview-image-wrap {
  flex-shrink: 0;
  width: 80px;
  height: 80px;
  overflow: hidden;
}

.capture-link-preview-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.capture-link-preview-body {
  flex: 1;
  min-width: 0;
  padding: var(--sp-sm);
  display: flex;
  flex-direction: column;
  gap: 4px;
  justify-content: center;
}

.capture-link-preview-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.capture-link-preview-desc {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ─────────────────────────────────────────────────────────────────────────
   Mobile Capture Sheet (full-height dark overlay)
   Only rendered on mobile via the is_mobile() FFI check.
   ───────────────────────────────────────────────────────────────────────── */

.capture-sheet-mobile {
  /* Explicit edges instead of shorthand `inset` for older Safari compat */
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* Contain all children — critical on iOS Safari to prevent rubber-band
     scroll leaking out of the fixed layer on both axes */
  overflow: hidden;
  /* Belt-and-suspenders: block overscroll bounce at the sheet level too */
  overscroll-behavior: none;
  /* iOS momentum scroll must never propagate to the document behind */
  touch-action: none;
  background: #000;
  z-index: 401;
  display: flex;
  flex-direction: column;
  /* Slide up on open */
  animation: sheet-slide-in 0.32s cubic-bezier(0.32, 0.72, 0, 1) forwards;
  /* Safe area bottom padding for home indicator */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* ── Floating close button ──────────────────────────────────────────────── */

.capture-sheet-close {
  position: absolute;
  top: calc(16px + env(safe-area-inset-top, 0px));
  right: 16px;
  z-index: 10;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: none;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
}

/* ── Body (fills all space between close btn and tabs) ─────────────────── */

.capture-sheet-body {
  flex: 1;
  min-height: 0;
  /* Prevent children from overflowing the flex column on iOS Safari */
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* ── Tab bar ─────────────────────────────────────────────────────────────── */

.capture-sheet-tabs {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 10px 0 12px;
  background: #000;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.capture-sheet-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.4);
  cursor: pointer;
  padding: 4px 0;
  transition: color 0.15s;
}

.capture-sheet-tab--active {
  color: #fff;
  font-weight: 600;
}

.capture-sheet-tab-label {
  font-size: 10px;
  letter-spacing: 0.01em;
  line-height: 1;
}

/* ── Link mode ───────────────────────────────────────────────────────────── */

.capture-sheet-link {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Hero: full-bleed OG image background */
.capture-sheet-hero {
  flex: 1;
  min-height: 0;
  position: relative;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding-bottom: 32px;
  /* Clip the background image and absolute overlays to this box */
  overflow: hidden;
  /* Gradient overlay so CTA text reads over bright images */
  background-color: #111;
}

.capture-sheet-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0,0,0,0.1) 0%,
    rgba(0,0,0,0.0) 40%,
    rgba(0,0,0,0.35) 100%
  );
  pointer-events: none;
}

/* Empty state placeholder */
.capture-sheet-hero--empty {
  background-color: #111;
}

/* Decorative circle — only visible once an OG image is loaded, positioned
   32px above the capture tab bar at the bottom of the sheet. */
.capture-sheet-hero:not(.capture-sheet-hero--empty)::before {
  content: "";
  display: block;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(255,255,255,0.08);
  position: absolute;
  bottom: 32px;
  left: 50%;
  transform: translateX(-50%);
}

/* "Use this link" CTA pill */
.capture-sheet-cta {
  position: relative;
  z-index: 1;
  background: rgba(255, 255, 255, 0.92);
  color: #000;
  font-size: var(--text-base);
  font-weight: 600;
  padding: 10px 28px;
  border-radius: var(--radius-full);
  border: none;
  cursor: pointer;
  letter-spacing: -0.01em;
  box-shadow: 0 2px 16px rgba(0,0,0,0.35);
  transition: transform 0.12s, opacity 0.12s;
}

.capture-sheet-cta:active {
  transform: scale(0.97);
}

.capture-sheet-cta--disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* URL input bar */
.capture-sheet-url-bar {
  flex-shrink: 0;
  padding: 14px 16px 10px;
  background: #000;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.capture-sheet-url-input {
  display: block;
  width: 100%;
  background: transparent;
  border: none;
  outline: none;
  color: #fff;
  font-size: var(--text-lg);
  font-family: var(--font-sans);
  /* Monospace-ish feel for URLs */
  letter-spacing: -0.01em;
  caret-color: #fff;
  padding: 0;
  /* Re-enable touch interaction — parent sets touch-action: none to lock
     document scroll, but inputs need touch events for focus / text selection */
  touch-action: auto;
}

.capture-sheet-url-input::placeholder {
  color: rgba(255, 255, 255, 0.3);
}

/* ── Shared form layout (Text / Voice / Gallery / Camera) ────────────────── */

.capture-sheet-form {
  flex: 1;
  min-height: 0;
  /* Scroll the form content when the keyboard shrinks the viewport;
     overscroll-behavior: contain stops bounce reaching the document */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  touch-action: pan-y;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
  padding: 56px 24px 24px; /* top pad clears close button */
  color: #fff;
}

.capture-sheet-input {
  width: 100%;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--radius-md);
  color: #fff;
  font: inherit;
  font-size: 16px;
  padding: 12px 14px;
  outline: none;
}

.capture-sheet-input::placeholder {
  color: rgba(255,255,255,0.35);
}

.capture-sheet-input:focus {
  border-color: rgba(255,255,255,0.3);
}

.capture-sheet-textarea {
  width: 100%;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--radius-md);
  color: #fff;
  font: inherit;
  font-size: 16px;
  padding: 12px 14px;
  outline: none;
  resize: none;
  min-height: 140px;
  touch-action: auto;
}

.capture-sheet-textarea::placeholder {
  color: rgba(255,255,255,0.35);
}

.capture-sheet-textarea:focus {
  border-color: rgba(255,255,255,0.3);
}

.capture-sheet-save-btn {
  width: 100%;
  padding: 14px;
  background: #fff;
  color: #000;
  font-size: var(--text-base);
  font-weight: 600;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: opacity 0.12s;
}

.capture-sheet-save-btn--disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Voice recorder */
.capture-sheet-voice-indicator {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
  color: rgba(255,255,255,0.6);
}

.capture-sheet-voice-recording {
  color: #ff453a;
  animation: pulse 1.2s ease-in-out infinite;
}

.capture-sheet-voice-timer {
  font-size: var(--text-2xl);
  font-weight: 300;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: #fff;
}

.capture-sheet-record-btn {
  padding: 12px 32px;
  background: rgba(255,255,255,0.1);
  color: #fff;
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: var(--radius-full);
  font-size: var(--text-base);
  font-weight: 500;
  cursor: pointer;
}

/* Camera label row (label + flip toggle) ----------------------------------- */
.capture-sheet-camera-label-row {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
}

/* Flip camera button (mobile sheet) */
.capture-sheet-flip-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius-full);
  color: rgba(255, 255, 255, 0.75);
  font: inherit;
  font-size: var(--text-sm);
  padding: 6px 14px 6px 10px;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.capture-sheet-flip-btn:active {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}
.capture-sheet-flip-btn .icon { color: rgba(255, 255, 255, 0.75); }

.capture-sheet-flip-label {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
}

/* Gallery / Camera file label — input is nested inside and overlays it */
.capture-sheet-file-label {
  position: relative; /* contains the absolute-positioned file input */
  overflow: hidden;   /* clips the input overlay to the label's border-radius */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
  padding: 40px 32px;
  border: 2px dashed rgba(255,255,255,0.18);
  border-radius: var(--radius-lg);
  color: rgba(255,255,255,0.6);
  font-size: var(--text-base);
  cursor: pointer;
  width: 100%;
  text-align: center;
  transition: border-color 0.15s, color 0.15s;
}

.capture-sheet-file-label:active {
  border-color: rgba(255,255,255,0.4);
  color: #fff;
}

.capture-sheet-error {
  color: #ff453a;
  font-size: var(--text-sm);
  text-align: center;
  margin: 0;
}

/* ── Text mode: Milkdown editor container ───────────────────────────────── */

/* Desktop (inside .capture-text-form) */
.capture-text-editor {
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  min-height: 128px;
  padding: var(--sp-sm);
  cursor: text;
  flex: 1;
  transition: border-color 0.12s;
  overflow-y: auto;
}

.capture-text-editor:focus-within {
  border-color: var(--bg-secondary);
  outline: none;
}

.capture-text-editor .ProseMirror {
  outline: none;
  min-height: 96px;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-primary);
  word-break: break-word;
}

/* Placeholder */
.capture-text-editor .ProseMirror p.is-editor-empty::before {
  content: "Write your note...";
  color: var(--text-tertiary);
  float: left;
  height: 0;
  pointer-events: none;
}

/* Inline formatting */
.capture-text-editor .ProseMirror strong { font-weight: 700; }
.capture-text-editor .ProseMirror em { font-style: italic; }
.capture-text-editor .ProseMirror code {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.9em;
  background: var(--bg-secondary);
  border-radius: 3px;
  padding: 1px 4px;
}

/* Block elements */
.capture-text-editor .ProseMirror h2 {
  font-size: 1.15em;
  font-weight: 700;
  margin: 0.5em 0 0.25em;
  line-height: 1.3;
}
.capture-text-editor .ProseMirror h3 {
  font-size: 1.05em;
  font-weight: 600;
  margin: 0.5em 0 0.25em;
  line-height: 1.3;
}
.capture-text-editor .ProseMirror p {
  margin: 0 0 0.4em;
}
.capture-text-editor .ProseMirror p:last-child { margin-bottom: 0; }
.capture-text-editor .ProseMirror ul,
.capture-text-editor .ProseMirror ol {
  margin: 0 0 0.4em;
  padding-left: 1.4em;
}

/* Mobile sheet variant */
.capture-text-editor--sheet {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.12);
  flex: 1;
  min-height: 0;
  width: 100%;
  /* Re-enable scroll inside the editor on iOS */
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
}

.capture-text-editor--sheet:focus-within {
  border-color: rgba(255, 255, 255, 0.3);
}

.capture-text-editor--sheet .ProseMirror {
  color: #fff;
  min-height: 80px;
}

.capture-text-editor--sheet .ProseMirror p.is-editor-empty::before {
  content: "Write your note...";
  color: rgba(255, 255, 255, 0.35);
}

.capture-text-editor--sheet .ProseMirror code {
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}

/* Mobile sheet text form layout override — top-align content */
.capture-sheet-form--text {
  justify-content: flex-start;
  /* A bit less top padding since toolbar + editor take up full height */
  padding-top: 52px;
}

/* ── Camera mode sheet — full-bleed getUserMedia viewfinder ─────────────── */

.capture-sheet-form--camera {
  padding: 0;
  justify-content: flex-start;
  overflow: hidden;
}

/* Viewfinder container fills all available height */
.capture-sheet-webcam-wrap {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
  background: #000;
}

/* Live preview video — covers the wrap div */
.capture-sheet-webcam-viewfinder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Shutter button — centred at the bottom of the viewfinder */
.capture-sheet-webcam-shutter {
  position: absolute;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  border: 3px solid rgba(255, 255, 255, 0.85);
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 0.12s;
}

.capture-sheet-webcam-shutter::after {
  content: "";
  position: absolute;
  inset: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transition: background 0.12s;
}

.capture-sheet-webcam-shutter--active::after {
  background: #fff;
}

.capture-sheet-webcam-shutter--active:active::after {
  opacity: 0.75;
}

.capture-sheet-webcam-shutter:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

/* Flip camera button — bottom right corner of the viewfinder */
.capture-sheet-webcam-flip {
  position: absolute;
  bottom: 40px;
  right: 24px;
  z-index: 2;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.12s;
}

.capture-sheet-webcam-flip:active { background: rgba(0, 0, 0, 0.65); }
.capture-sheet-webcam-flip .icon { color: #fff; }

/* Photo preview after shutter — full-bleed image + action buttons */
.capture-sheet-webcam-preview {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
  background: #000;
  display: flex;
  flex-direction: column;
}

.capture-sheet-webcam-preview-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Action row overlaid at the bottom of the preview */
.capture-sheet-webcam-preview-actions {
  position: relative;
  z-index: 1;
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  padding: 20px 24px 28px;
  background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 100%);
}

.capture-sheet-webcam-retake {
  align-self: center;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: var(--radius-full);
  color: #fff;
  font: inherit;
  font-size: var(--text-sm);
  padding: var(--sp-xs) var(--sp-xl);
  cursor: pointer;
  transition: background 0.12s;
}

.capture-sheet-webcam-retake:active {
  background: rgba(0, 0, 0, 0.65);
}

/* ============================================================================
   Chat feature: thread-view pane (header, messages list, composer).

   Layout: the chat module renders into the main-pane slot in the app shell,
   taking the full available height. The inner content is capped to a
   readable column width and centered.
   ========================================================================= */

.chat {
  display: grid;
  /* row 1: header | row 2: messages | row 3: mobile-edit cancel bar (collapses when empty) | row 4: composer */
  grid-template-rows: auto 1fr auto auto;
  /* Inherit height from the .main-pane flex chain rather than setting our
     own dvh value. The shell is sized via --vvh (visualViewport.height) in
     dom.mjs, so this correctly reflects the keyboard-adjusted visible area
     on iOS Safari. */
  height: 100%;
  width: 100%;
  position: relative;
  overflow: hidden;
  background: var(--bg-secondary);

}

/* Floating "jump to latest" pill that appears when a new message lands
   while the viewer is scrolled away from the bottom. */
.chat-jump-to-latest {
  position: absolute;
  left: 50%;
  bottom: 72px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: var(--sp-xs) var(--sp-sm);
  background: var(--bg-elevated, var(--bg));
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
  border-radius: 999px;
  font-size: 0.85rem;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 2;
}
@media (hover: hover) {
  .chat-jump-to-latest:hover {
    background: var(--bg-hover);
  }
}

/* Floating scroll-to-bottom button — appears when the user scrolls up.
   Right-aligned to the messages column, 56px above the composer. */
.chat-scroll-to-bottom {
  position: absolute;
  right: var(--sp-xl, 24px);
  bottom: 96px;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-primary, var(--bg-secondary));
  color: var(--text-primary);
  border-radius: var(--radius-xs, 8px);
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 10;
  padding: 0;
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
}
.chat-scroll-to-bottom--hidden {
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
}
@media (hover: hover) {
  .chat-scroll-to-bottom:hover {
  background: var(--bg-hover, var(--bg-primary));
}
}

/* ── Header ───────────────────────────────────────────────────────────── */

/* The thread header is the standardized ui/page_header component
   (.page-header + slots, shell.css) — back link (desktop) / sidebar
   toggle (mobile) on the left, settings gear (+ group ⋮ menu) on the
   right. Scoped additions: pin it to the .chat grid's first row and keep
   the old .chat-header bottom inset above the messages list (the shared
   .page-header has no bottom padding). */
.chat > .page-header {
  grid-row: 1;
  padding-bottom: var(--sp-md);
}

/* Thread title is a <button> (tap → Chat Settings), not an <a>. Layout
   and typography come from .page-header-title / .page-header-title-link
   (shell.css); this only strips the native button chrome those
   <a>-oriented classes don't cover. */
.chat-header-title-link {
  background: transparent;
  border: none;
  font-family: inherit;
  line-height: inherit;
  padding: 0;
}

/* The settings gear and ⋮ menu buttons get their sizing/color/radius/
   hover from .header-icon-btn; the page-header right slot handles their
   alignment and spacing. */

/* ── Messages ─────────────────────────────────────────────────────────── */

.chat-messages {
  grid-row: 2;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--sp-xl);
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE/Edge */
  max-width: 800px;
  width: 100%;
  justify-self: center;
}
.chat-messages::-webkit-scrollbar {
  display: none; /* Chrome, Safari, Opera */
}

/* Each message row: avatar column + content stack. The whole row
   reverses for sent messages so the stack hugs the right edge. */
.chat-message-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-xs);
  max-width: 70%;
  min-width: 0; /* prevent flex item from overriding max-width with intrinsic size */
}

.chat-message-row.received {
  align-self: flex-start;
}

.chat-message-row.sent {
  align-self: flex-end;
  /* flex-direction: row-reverse; */
}

.chat-message-row.ai-bare {
  align-self: stretch;
  max-width: 100%;
}

/* Highlight when the current viewer is @-mentioned — iOS parity: a
   full-bleed soft green band across the whole chat column with a solid
   green accent bar on the left edge. The bubble itself stays untinted;
   the band carries the emphasis.
   Geometry: rows are normally capped at 70% width, so the mentioned row
   un-caps to full width and cancels the .chat-messages horizontal
   padding with negative margins (restored as padding so content stays
   aligned with its neighbors). Bubbles keep their own width via
   .chat-message-stack — the row stretching doesn't stretch them. */
.chat-message-row.mentioned-me {
  max-width: none;
  width: calc(100% + 2 * var(--sp-xl));
  align-self: stretch;
  margin-inline: calc(var(--sp-xl) * -1);
  padding-inline: calc(var(--sp-xl) - 3px) var(--sp-xl);
  padding-block: var(--sp-xs);
  border-left: 3px solid #62d475; /* brand green (fill-gradient top stop) */
  border-radius: 0;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, #62d475 14%, transparent) 0%,
    color-mix(in srgb, #62d475 8%, transparent) 100%
  );
}

/* Sent-side mentions (self-mention edge case) keep right alignment of
   the bubble inside the full-width band. */
.chat-message-row.sent.mentioned-me {
  justify-content: flex-end;
}

/* The row un-capping must not let bubbles grow past their normal
   width — restore the 70% cap at the stack level inside the band. */
.chat-message-row.mentioned-me .chat-message-stack {
  max-width: 70%;
}

/* Bubbles inside the band swap to the elevated fill so they read as a
   surface on top of the green wash (token themes both modes; no filter —
   filters create a containing block that can mis-anchor bubble popovers). */
.chat-message-row.mentioned-me .chat-message-bubble {
  background: var(--fill-elevated-secondary);
}

.chat-timestamp-header {
  align-self: center;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  padding: var(--sp-sm) 0 var(--sp-3xs);
  text-align: center;
}

/* Fixed-width avatar column. Empty for continuation rows so bubbles
   indent under the group's avatar. */
.chat-message-avatar-slot {
  width: 28px;
  flex-shrink: 0;
  align-self: flex-start;
  display: flex;
  justify-content: center;
}

.chat-message-avatar-ai {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--fg);
  color: var(--bg);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
}

/* Stack: tiny timestamp + bubble (and reaction wrapper). */
.chat-message-stack {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.chat-message-row.sent .chat-message-stack {
  align-items: flex-end;
}

.chat-message-tiny-time {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  padding: 0 var(--sp-xs);
  margin-bottom: var(--sp-3xs);
}

.chat-message-row.sent .chat-message-tiny-time {
  text-align: right;
}

/* Sender name + tiny time, inline, for the first message of a group (see
   chat.gleam's `header_el`/`show_sender_name`) — both received messages
   and the viewer's own sent messages. The name owns the row's spacing;
   the nested .chat-message-tiny-time keeps its own rules but no longer
   needs its own bottom margin since the row provides it. */
.chat-message-header-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3xs);
  margin-bottom: var(--sp-3xs);
}

.chat-message-header-row .chat-message-tiny-time {
  margin-bottom: 0;
  padding: 0;
}

.chat-message-sender-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  padding-left: var(--sp-xs);
}

/* Bullet separator between the sender name and the tiny time — same
   footprint as .chat-message-tiny-time itself (exact spec, not just
   inherited), so it reads as part of that same tiny-time treatment. */
.chat-message-header-bullet {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  margin-bottom: 0;
  padding: 0;
}

/* "Edited" suffix — same size/colour as the time, italic only */
.chat-message-tiny-time__edited {
  font-style: italic;
  font-size: inherit;
  color: inherit;
}

.chat-message-bubble {
  padding: var(--sp-sm) var(--sp-md);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
  line-height: 1.5;
  overflow-wrap: anywhere; /* break long URLs/tokens at any character; also affects flex min-content */
  min-width: 0;
}

/* Continuation bubbles inside a group sit a touch closer to the
   one above. */
.chat-message-row + .chat-message-row .chat-message-bubble {
  margin-top: var(--sp-3xs);
}

.chat-message-row.received .chat-message-bubble {
  border-top-left-radius: var(--radius-sm);
  background: var(--bg-quarternary);
  color: var(--text-primary);
  backdrop-filter: contrast(.8);
}

.chat-message-row.sent .chat-message-bubble {
  border-top-right-radius: var(--radius-sm);
  background: var(--bg-primary);
  color: var(--text-primary);
  background-blend-mode: var(--blend-mode);
}

/* AI bare: no bubble background; full-width content. Avatar slot
   stays so the row keeps its left-column alignment. */
.chat-message-row.ai-bare .chat-message-stack {
  width: 100%;
}
.chat-message-row.ai-bare .chat-message-bubble {
  padding: 0;
  background: transparent;
  border-radius: 0;
}
.chat-message-row:not(.ai-bare) + .chat-message-row.ai-bare {
  padding-top: var(--sp-md);
}
.chat-message-row.ai-bare .chat-message-text {
  line-height: 1.65;
}
.chat-message-row.ai-bare .chat-message-text p {
  margin: 0 0 var(--sp-sm) 0;
}

.chat-message-ai-bare .chat-message-text ul,
.chat-message-ai-bare .chat-message-text ol,
.chat-message-ai-bare .chat-message-text blockquote,
.chat-message-ai-bare .chat-message-text pre {
  margin-top: -4px;
  margin-right: 0;
  margin-bottom: var(--sp-sm);
  margin-left: 0;
}

/* ── Reactions ───────────────────────────────────────────────────────── */

.chat-message-bubble-wrapper {
  position: relative;
}

.chat-reaction-trigger {
  position: absolute;
  top: -22px;
  /* Anchor to the bubble's LEFT edge so the pill grows RIGHTWARD — the
     open side for received messages (they hug the pane's left). Pinning
     right: -4px grew the pill leftward, so any bubble narrower than the
     pill clipped it against the scroll container's left edge. */
  left: -4px;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-sm);
  border-radius: var(--radius-full);
  background: var(--bg);
  border: 1px solid var(--border-secondary);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  opacity: 0;
  /* Keep the hidden trigger out of tab order and pointer reach so keyboard
     users can't focus an invisible control. */
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.3s ease-in-out;
  color: var(--text-tertiary);
  white-space: nowrap;
}

.reaction-trigger__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: inherit;
}

.reaction-trigger__label {
  font-size: 13px;
  line-height: 1;
  color: inherit;
  font-weight: 500;
}

.chat-reaction-trigger:focus-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
@media (hover: hover) {
  .chat-message-bubble-wrapper:hover .chat-reaction-trigger {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
}

@media (hover: hover) {
  .chat-reaction-trigger:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
}

/* Sent messages hug the pane's right — mirror the anchor so the pill
   grows LEFTWARD into open space (a left anchor overflowed the right
   edge on bubbles narrower than the pill). */
.chat-message-row.sent .chat-reaction-trigger {
  left: auto;
  right: -4px;
}

/* Hide the trigger whenever the emoji pill is open for this message —
   the pill replaces it rather than overlaying it. */
.chat-message-bubble-wrapper:has(.emoji-picker-pill) .chat-reaction-trigger {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.chat-reaction-picker {
  position: absolute;
  top: -40px;
  left: 0;
  display: flex;
  gap: var(--sp-3xs);
  background: var(--bg);
  border: 1px solid var(--border-secondary);
  border-radius: var(--radius-lg);
  padding: var(--sp-3xs) var(--sp-xs);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  z-index: 10;
}

.chat-message-row.sent .chat-reaction-picker {
  left: auto;
  right: 0;
}

.chat-reaction-picker-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: 16px;
  padding: 0;
  transition: background 0.1s ease, transform 0.1s ease;
}

@media (hover: hover) {
  .chat-reaction-picker-btn:hover {
  background: var(--bg-subtle);
  transform: scale(1.2);
}
}

.chat-reaction-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: var(--sp-3xs);
}

.chat-message-row.sent .chat-reaction-pills {
  justify-content: flex-end;
}

.chat-reaction-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xxs);
  padding: var(--size-xxs) var(--size-sm);
  border-color: transparent;
  border-radius: var(--radius-full);
  background: var(--fill-elevated-secondary);
  font-size: var(--text-xs);
  cursor: pointer;
  touch-action: none; /* deliver pointer events immediately — required for long-press */
  transition: background 0.1s ease, border-color 0.1s ease;
}

.chat-reaction-pill.active {
  background: rgba(var(--color-blue), .2);
  background-blend-mode: var(--blend-mode);
}

@media (hover: hover) {
  .chat-reaction-pill:hover {
  background: var(--fill-elevated-primary);
  border-color: var(--border-secondary);
}
}

@media (hover: hover) {
  .chat-reaction-pill.active:hover {
  background: rgba(var(--color-blue), .4);
  border-color: var(--accent-blue);
}
}


/* BEM-style child spans — `.reaction-pill__emoji` holds the emoji glyph,
   `.reaction-pill__count` holds the numeric count. The pill itself uses
   inline-flex + gap to space them. `tabular-nums` keeps the count from
   jitter-shifting as digits change width. */
.reaction-pill__emoji,
.reaction-pill__count {
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

.reaction-pill__emoji {
  font-size: 16px;
}

.reaction-pill__count {
  font-variant-numeric: tabular-nums;
}


.chat-reaction-tooltip {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 0;
  padding: var(--size-sm) var(--size-md);
  border-radius: 6px;
  background: var(--fill-primary);
  color: var(--text-secondary);
  font-size: var(--text-base);
  font-weight: 500;
  line-height: 1.3;
  max-width: 240px;
  width: max-content;
  white-space: normal;
  word-wrap: break-word;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.08s ease;
  z-index: 10;
}

.chat-message-row.sent .chat-reaction-tooltip {
  left: auto;
  right: 0;
}

.chat-reaction-pill:focus-visible .chat-reaction-tooltip {
  opacity: 1;
  visibility: visible;
}
@media (hover: hover) {
  .chat-reaction-pill:hover .chat-reaction-tooltip {
  opacity: 1;
  visibility: visible;
}
}

.chat-reaction-tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 12px;
  border: 4px solid transparent;
  border-top-color: var(--fill-primary);
}

.chat-message-row.sent .chat-reaction-tooltip::after {
  left: auto;
  right: 12px;
  border-top-color: var(--fill-primary);
}

/* ── Composer ─────────────────────────────────────────────────────────── */

.chat-composer {
  grid-row: 4;
  width: 100%;
  max-width: 800px;
  padding: 0 var(--sp-xl) var(--sp-xxl) var(--sp-xl);
  justify-self: center;
  position: relative;
}

/* ── @-mention picker ────────────────────────────────────────────────────── */

.chat-mention-picker {
  position: absolute;
  left: var(--sp-xl);
  right: var(--sp-xl);
  bottom: calc(100% - var(--sp-xxl));
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  max-height: 240px;
  overflow-y: auto;
  z-index: 20;
}

.chat-mention-picker-item {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md);
  cursor: pointer;
}
@media (hover: hover) {
  .chat-mention-picker-item:hover {
  background: var(--bg-subtle);
}
}
/* Keyboard-highlighted row (↑/↓ navigation) — elevated secondary fill. */
.chat-mention-picker-item--active {
  background: var(--fill-elevated-secondary);
}
.chat-mention-picker-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.chat-mention-picker-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-mention-picker-username {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* On iOS the home-indicator bar sits below the safe area and overlaps the
   composer. A small bottom margin keeps the form above it. */
.ios .chat-composer {
  margin-bottom: var(--sp-4xs);
}

/* Mobile bottom spacing — two states:
   • Keyboard closed: clear the home indicator (safe-area inset, with a
     sp-md floor for devices without one).
   • Keyboard open (.kb-open, set by the --vvh updater in dom.mjs): the
     keyboard covers the home-indicator zone, but env() does NOT collapse
     — dropping to a slim pad stops the composer floating high above the
     keyboard. */
@media (max-width: 768px) {
  .chat-composer {
    padding-bottom: max(var(--sp-md), env(safe-area-inset-bottom));
  }
  .kb-open .chat-composer {
    padding-bottom: var(--sp-sm);
  }
  .kb-open.ios .chat-composer {
    margin-bottom: 0;
  }
}

/* Inner wrapper — takes up the flex space the textarea used to own */
.composer-inline-input-wrap {
  flex: 1;
  min-width: 0;
  position: relative;
}

/* Override flex: 1 on the textarea so it fills the block wrapper instead */
.composer-inline-input-wrap .chat-composer-input {
  flex: none;
  width: 100%;
}

/* When there's content and the composer is not focused: hide raw markdown
   so only the rendered overlay is visible. Caret-color is also cleared so
   the invisible cursor doesn't flicker when blurred. */
.composer-inline-input-wrap--has-content:not(:focus-within) .chat-composer-input {
  color: transparent;
  caret-color: transparent;
}

/* Rendered markdown overlay — absolutely positioned over the textarea.
   pointer-events: none lets clicks fall through to the textarea, which
   focuses it and triggers :focus-within to switch back to edit mode. */
.composer-inline-preview {
  position: absolute;
  top: 6px;       /* matches textarea padding-top */
  left: 0;
  right: 0;
  pointer-events: none;
  font-size: var(--text-lg);
  line-height: 20px;
  color: var(--text-primary);
  word-break: break-word;
  white-space: pre-wrap;
  overflow: hidden;
  max-height: 250px;
}

/* Strip paragraph margins that marked.parse adds */
.composer-inline-preview p {
  margin: 0;
}

/* Re-enable list bullets/numbers overridden by the base.css `ul, ol { list-style: none }` reset */
.composer-inline-preview ul,
.composer-inline-preview ol {
  padding-left: 1.4em;
}
.composer-inline-preview ul { list-style-type: disc; }
.composer-inline-preview ol { list-style-type: decimal; }

/* Switch to edit mode while the textarea has focus */
.composer-inline-input-wrap:focus-within .composer-inline-preview {
  display: none;
}

/* Unified pill — the form IS the composer box.
   Column layout: [strip?] [input] [toolbar] */
.chat-composer-form {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--bg-primary);
  border: 1px solid var(--border-secondary);
  border-radius: var(--radius-xl);
  transition: border-color 0.15s ease, background 0.15s ease;
  /* No overflow: hidden here — the emoji picker pops above the pill and
     must not be clipped. Corner clipping is handled on inner chips. */
  position: relative;
}

.chat-composer-form.drag-over {
  border-color: var(--bg-secondary);
  background: var(--bg-secondary);
}

/* Emoji picker — anchored above the pill, right-aligned */
.chat-composer-form .emoji-picker {
  position: absolute;
  bottom: calc(100% + var(--sp-sm));
  right: 0;
  width: 50%;
  max-width: none;
}

/* Input row — no background or border, just spacing */
.chat-composer-input-wrap {
  flex: 1;
  position: relative;
  display: flex;
  align-items: flex-end;
  gap: var(--sp-xs);
  padding: var(--sp-sm) var(--sp-sm) 0 var(--sp-sm);
  min-width: 0;
}

.chat-composer-file-input {
  display: none;
}

/* Staged media preview strip — slides in at top of pill when an attachment
   is staged. Horizontal scroll for multiple chips. */
.chat-composer--mediaPreviewStrip {
  display: flex;
  flex-direction: row;
  gap: var(--sp-sm);
  overflow-x: auto;
  padding: var(--sp-sm) var(--sp-sm) 0 var(--sp-sm);
  scrollbar-width: none;
  /* Slide-up entrance animation */
  animation: composerStripIn 180ms ease;
}
.chat-composer--mediaPreviewStrip::-webkit-scrollbar {
  display: none;
}

@keyframes composerStripIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Bottom toolbar row — attach | emoji | spacer | send */
.chat-composer-toolbar {
  display: flex;
  flex-direction: row;
  align-items: center;
  padding: var(--sp-xs) var(--sp-xs);
  gap: var(--sp-3xs);
}

.chat-composer-toolbar-spacer {
  flex: 1;
}

/* Attach (+) button — inside the pill, icon-only, no border */
.chat-composer-add {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: color 120ms ease, background 120ms ease;
}
@media (hover: hover) {
  .chat-composer-add:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
}

.chat-composer--mediaPreview {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-primary);
  background: var(--bg-subtle);
  overflow: hidden;
  width: 80px;
  height: 80px;
}

.chat-composer-pending-image-thumb {
  display: block;
  width: 80px;
  height: 80px;
  object-fit: cover;
  border-radius: calc(var(--radius-md) - 1px);
}

/* Invisible button wrapper around the composer thumbnail — opens lightbox on tap */
.chat-composer-pending-image-preview-btn {
  display: block;
  padding: 0;
  margin: 0;
  border: none;
  background: none;
  cursor: zoom-in;
  width: 100%;
  height: 100%;
  line-height: 0;
}

.chat-composer-pending-image-remove {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-full);
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  line-height: 0;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

@media (hover: hover) {
  .chat-composer-pending-image-remove:hover {
  background: rgba(0, 0, 0, 0.75);
}
}

/* Textarea — transparent, no border, fills the pill */
.chat-composer-input {
  flex: 1;
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  outline: none;
  min-width: 0;
  resize: none;
  max-height: 250px;
  overflow-y: auto;
  line-height: 20px;
  padding: 6px 0;
  margin: 0;
}
.chat-composer-input::placeholder {
  color: var(--text-secondary);
}

/* Emoji button — inside the toolbar row */
.chat-composer-emoji-btn {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  padding: 0;
  border-radius: var(--radius-full);
  transition: color 120ms ease, background 120ms ease;
}
@media (hover: hover) {
  .chat-composer-emoji-btn:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
}

/* Send button — inside the toolbar row, right side */
.chat-composer-send {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
  transition: background 0.15s ease;
  /* Enabled state */
  background: var(--bg-primary-inverse);
  color: var(--text-primary-inverse);
}

.chat-composer-send:disabled {
  background: var(--bg-quarternary);
  color: var(--text-disabled);
  cursor: default;
}

/* ── Empty / error state (e.g. failed message load) ──────────────────── */

.chat-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: var(--sp-xl);
}

/* (Header ⋮ dropdown removed — Delete chat lives in the Chat Settings
   sheet's footer, behind a confirmation.) */

/* ── Message attachments ── */

.chat-att {
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: var(--sp-xs);
}

.chat-att:last-child { margin-bottom: 0; }

/* Image */
.chat-att-image {
  position: relative;
  max-width: 220px;
  overflow: hidden;
}

.chat-att-image-btn {
  display: block;
  padding: 0;
  border: none;
  background: transparent;
  cursor: zoom-in;
  width: 100%;
}

.chat-att-img {
  display: block;
  width: 100%;
  max-width: 100%; /* prevent full-res intrinsic size from breaking out of container */
  height: auto;
  border-radius: var(--radius-md);
}

/* Link unfurl — favicon only. Stream's "image" attachment type resolved to
   a small square icon rather than a real og:image, so it's shown inline
   next to the raw link text instead of as a big media card. */
.chat-att-favicon {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  max-width: 100%;
  text-decoration: none;
}
@media (hover: hover) {
  .chat-att-favicon:hover {
  text-decoration: none;
}
}

.chat-att-favicon-icon {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.chat-att-favicon-link {
  color: var(--accent-blue);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}
@media (hover: hover) {
  .chat-att-favicon:hover .chat-att-favicon-link {
  text-decoration: underline;
}
}

/* Link unfurl — real og:image preview. The whole card is the image, linked
   to the source; no title/description text (tap the image to navigate). */
.chat-att-image-link {
  display: block;
  max-width: 280px;
}

.chat-att-image-link-img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
}

/* Link unfurl — animated gif. Same aspect-ratio media treatment as the
   image-link card, plus a "View at: site.domain" caption overlaid at the
   bottom so the source (Tenor, Giphy, etc.) stays visible. */
.chat-att-gif {
  display: block;
  position: relative;
  max-width: 280px;
}

.chat-att-gif-img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
}

.chat-att-gif-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: var(--sp-lg) var(--sp-sm) var(--sp-xs);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  font-size: var(--text-xs);
  pointer-events: none;
}

.chat-att-gif-caption-label {
  font-weight: 400;
  color: var(--text-secondary);
}

.chat-att-gif-caption-domain {
  font-weight: 600;
  color: var(--accent-blue);
}

/* Corner download button shared by image + video attachments. */
.chat-att-download {
  position: absolute;
  top: var(--sp-xs);
  right: var(--sp-xs);
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  border: none;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  opacity: 0;
  transition: opacity 120ms ease;
}

.chat-att-download:focus-visible {
  opacity: 1;
}
@media (hover: hover) {
  .chat-att-image:hover .chat-att-download,
  .chat-att-video:hover .chat-att-download {
  opacity: 1;
}
}

@media (hover: hover) {
  .chat-att-download:hover {
  background: rgba(0, 0, 0, 0.75);
}
}

/* Lightbox overlay for full-size image preview */
.chat-image-preview-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  cursor: zoom-out;
  padding: var(--sp-xl);
}

.chat-image-preview-img {
  max-width: 95%;
  max-height: 95%;
  object-fit: contain;
  border-radius: var(--radius-md);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

.chat-image-preview-close {
  position: fixed;
  top: var(--sp-md);
  right: var(--sp-md);
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  border: none;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

@media (hover: hover) {
  .chat-image-preview-close:hover {
  background: rgba(255, 255, 255, 0.25);
}
}

/* Video */
.chat-att-video {
  position: relative;
  max-width: 320px;
  overflow: hidden;
}

/* Click-to-preview thumbnail — same shape as .chat-att-image-btn, but with
   a centered play icon signalling it opens the fullscreen video lightbox
   rather than zooming a static image. */
.chat-att-video-btn {
  display: block;
  position: relative;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  width: 100%;
}

.chat-att-video-thumb {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-md);
  background: #000;
}

.chat-att-video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.chat-att-placeholder {
  width: 220px;
  height: 160px;
  background: var(--bg-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-tertiary);
}

/* File */
.chat-att-file {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-sm);
  background: var(--bg-subtle);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  color: inherit;
  cursor: pointer;
  text-align: left;
  width: 100%;
}

@media (hover: hover) {
  .chat-att-file:hover {
  background: var(--bg-elevated, var(--bg-subtle));
}
}

.chat-att-file-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.chat-att-file-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-att-file-size {
  color: var(--text-secondary);
}

/* Link preview */
.chat-att-link {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-primary);
  text-decoration: none;
  color: inherit;
  max-width: 260px;
}
@media (hover: hover) {
  .chat-att-link:hover {
  border-color: var(--border-primary);
  text-decoration: none;
}
}

.chat-att-link-image {
  width: 100%;
  height: 120px;
  object-fit: cover;
}

.chat-att-link-body {
  padding: var(--sp-xs) var(--sp-sm);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.chat-att-link-title {
  font-size: var(--text-sm);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-att-link-desc {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Feed content card (Please-specific) */
.chat-att-content {
  display: flex;
  border: 1px solid var(--border-primary);
  text-decoration: none;
  color: inherit;
  max-width: 260px;
  gap: var(--sp-sm);
}
@media (hover: hover) {
  .chat-att-content:hover {
  border-color: var(--border-primary);
  text-decoration: none;
}
}

.chat-att-content-thumb {
  width: 80px;
  height: 80px;
  object-fit: cover;
  flex-shrink: 0;
}

.chat-att-content-thumb-placeholder {
  width: 80px;
  height: 80px;
  background: var(--bg-subtle);
  flex-shrink: 0;
}

.chat-att-content-body {
  padding: var(--sp-xs) var(--sp-xs) var(--sp-xs) 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.chat-att-content-category {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
}

.chat-att-content-title {
  font-size: var(--text-sm);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-att-content-likes {
  display: flex;
  align-items: center;
  gap: 3px;
  font-size: 10px;
  color: var(--text-tertiary);
}

/* Message text alongside attachments */
.chat-message-text {
  margin: 0;
}

.chat-message-text:has(+ .chat-att) {
  margin-bottom: var(--sp-xs);
}

/* Links in chat message text use the cobalt accent */

.chat-message-text a {
  color: var(--accent-blue);
  text-decoration: none;
  overflow-wrap: anywhere;
}

@media (hover: hover) {
  .chat-message-text a:hover {
  text-decoration: underline;
}
}

/* ─── Video embed attachment ─────────────────────────────────────── */

/* Outer wrapper: left thread-line, gap, stacked content */
.chat-embed-wrapper {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-left: calc(8px + var(--sp-xs));
  border-left: 2px solid var(--border);
  margin-bottom: var(--sp-xs);
}

/* When an embed follows message text, give it breathing room */
.chat-message-text + .chat-embed-wrapper {
  margin-top: 12px;
}

/* Brand logo — max-width 72px, display as block so the SVG scales */
.brand-logo {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  margin-bottom: var(--sp-xs);
}

.brand-logo svg {
  width: 100%;
  height: auto;
}

.brand-logo-youtube {
  max-width: 72px;
}

.brand-logo-vimeo {
  max-width: 60px;
}

/* Header row: title + chevron toggle */
.chat-embed-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-xs);
  margin-bottom: 12px;
}

.chat-embed-title {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--accent-cobalt);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  flex: 1;
  min-width: 0;
}

.chat-embed-toggle {
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 2px;
  cursor: pointer;
  color: var(--text-tertiary);
  display: flex;
  align-items: center;
  border-radius: var(--radius-sm);
}

@media (hover: hover) {
  .chat-embed-toggle:hover {
  color: var(--text-primary);
  background: var(--bg-secondary);
}
}

/* Collapsible area: iframe + link-out */
.chat-embed-collapsible {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow: hidden;
  max-width: 420px;
  max-height: 500px;
  transition: max-height 0.3s ease;
}

.chat-embed-collapsible--collapsed {
  max-height: 0;
}

/* Responsive iframe container via aspect-ratio */
.chat-embed-player-wrap {
  position: relative;
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
}

.chat-embed-iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
  border-radius: var(--radius-md);
}

/* "Open in YouTube" link-out row */
.chat-embed-linkout {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  color: var(--accent-cobalt);
  text-decoration: none;
  width: fit-content;
}

@media (hover: hover) {
  .chat-embed-linkout:hover {
  text-decoration: underline;
}
}

/* ============================================================================
   New Chat modal — contact picker overlay
   ========================================================================= */

.new-chat-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Must layer above the settings sheet (401 — sheet.css desktop block)
     when opened from the Add Members flow. */
  z-index: 410;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  /* When this overlay renders inside the chat-settings sheet's
     `.slide-up-sheet-shell` (Add Members flow), it inherits the parent's
     `pointer-events: none` and clicks fall through to the sheet beneath
     it. Re-enable here. */
  pointer-events: auto;
}

.new-chat-modal {
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  width: min(440px, calc(100vw - 48px));
  max-height: calc(100vh - 48px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Sheet drag handle: mobile-only (the modal becomes a slide-up sheet). */
.new-chat-handle {
  display: none;
}

@media (max-width: 768px) {
  /* Bottom-sheet presentation: pin the modal to the bottom edge and reuse
     the shared slide-up-sheet look + entrance animation (sheet.css). The
     drag-to-dismiss is attached on open via attach_sheet_drag. */
  .new-chat-overlay {
    align-items: flex-end;
  }
  .new-chat-modal {
    width: 100%;
    max-width: none;
    max-height: 95vh;
    min-height: 30vh;
    border: none;
    box-shadow: none;
    border-radius: 20px 20px 0 0;
    animation: sheet-slide-in 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;
    will-change: height;
  }
  .new-chat-handle {
    display: flex;
  }
}

.new-chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-lg) var(--sp-lg) var(--sp-sm);
}

.new-chat-title {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
}

.new-chat-close {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-tertiary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
@media (hover: hover) {
  .new-chat-close:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
}

.new-chat-search {
  margin: 0 var(--sp-lg);
  padding: var(--sp-xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  font-family: inherit;
  font-size: var(--text-lg); /* 16px */
  color: var(--text-primary);
  outline: none;
}
.new-chat-search:focus {
  border-color: var(--bg-secondary);
}
.new-chat-search::placeholder {
  color: var(--text-secondary);
}

.new-chat-contacts {
  flex: 1;
  overflow-y: auto;
  list-style: none;
  margin: var(--sp-sm) 0 0;
  padding: 0 var(--sp-sm);
  min-height: 200px;
  max-height: 360px;
}

.new-chat-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-xl);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.new-chat-contact-row {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-md);
  cursor: pointer;
  user-select: none;
  border: 1px solid transparent;
}
@media (hover: hover) {
  .new-chat-contact-row:hover {
  background: var(--bg-subtle);
}
}

.new-chat-contact-selected {
  background: rgba(var(--accent-rgb, 0, 0, 0), 0.06);
  border-color: rgba(var(--accent-rgb, 0, 0, 0), 0.2);
}
@media (hover: hover) {
  .new-chat-contact-selected:hover {
  background: rgba(var(--accent-rgb, 0, 0, 0), 0.06);
  border-color: rgba(var(--accent-rgb, 0, 0, 0), 0.2);
}
}

.new-chat-contact-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.new-chat-contact-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.new-chat-contact-username {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.new-chat-contact-check {
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent);
  flex-shrink: 0;
}

.new-chat-footer {
  padding: var(--sp-sm) var(--sp-lg) var(--sp-lg);
}

.new-chat-create {
  width: 100%;
  padding: var(--sp-xs) var(--sp-md);
  background: var(--accent);
  color: var(--accent-fg);
  border: none;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: opacity 120ms ease, background 120ms ease;
}
@media (hover: hover) {
  .new-chat-create:hover:not(:disabled) {
  opacity: 0.9;
}
}
.new-chat-create:disabled {
  background: var(--bg-secondary);
  color: var(--text-tertiary);
  cursor: not-allowed;
}

/* ── Share to chat ── */

.share-chat-spinner {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  animation: share-chat-pulse 1s ease-in-out infinite;
}

.share-chat-disabled {
  opacity: 0.5;
  pointer-events: none;
  cursor: default;
}

/* The three destination sources share one scroll region. */
.share-sections {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: var(--sp-sm);
}

.share-section {
  margin-top: var(--sp-md);
}
.share-section:first-child {
  margin-top: var(--sp-xs);
}

.share-section-title {
  margin: 0;
  padding: 0 var(--sp-lg) var(--sp-3xs);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
}

/* Inside the shared scroll region each list sizes to its content rather
   than carrying its own min-height/scroll (which stacked badly). */
.share-section .new-chat-contacts {
  flex: none;
  min-height: 0;
  max-height: none;
  overflow: visible;
  margin: 0;
}
.share-section .new-chat-empty {
  padding: var(--sp-md) var(--sp-lg);
  justify-content: flex-start;
}

.share-chat-selected {
  background: rgba(var(--accent-rgb, 0, 0, 0), 0.06);
  border-color: rgba(var(--accent-rgb, 0, 0, 0), 0.2);
}
@media (hover: hover) {
  .share-chat-selected:hover {
  background: rgba(var(--accent-rgb, 0, 0, 0), 0.06);
  border-color: rgba(var(--accent-rgb, 0, 0, 0), 0.2);
}
}

/* "Send in new group" secondary action above the primary Send button. */
.new-chat-secondary {
  width: 100%;
  padding: var(--sp-xs) var(--sp-md);
  margin-bottom: var(--sp-xs);
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}
@media (hover: hover) {
  .new-chat-secondary:hover {
  background: var(--bg-subtle);
}
}

/* Inline new-group name composer in the footer. */
.share-composer {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
}
.share-composer .new-chat-search {
  flex: 1;
  margin: 0;
}
.share-composer .new-chat-create {
  width: auto;
  white-space: nowrap;
}
.new-chat-cancel {
  padding: var(--sp-xs) var(--sp-md);
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  white-space: nowrap;
}
@media (hover: hover) {
  .new-chat-cancel:hover {
  background: var(--bg-subtle);
}
}

/* ── Typing indicator ── */

.chat-typing {
  min-height: 24px;
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: 0 var(--sp-xl);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.chat-typing-dots {
  display: flex;
  gap: 3px;
}

.chat-typing-dot {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: var(--text-tertiary);
  animation: typing-bounce 1.2s ease infinite;
}

.chat-typing-dot:nth-child(2) { animation-delay: 0.2s; }
.chat-typing-dot:nth-child(3) { animation-delay: 0.4s; }

/* ── Markdown in messages ── */

.chat-message-text {
  line-height: 1.5;
}

.chat-message-text p {
  margin: 0 0 var(--sp-xs) 0;
}
.chat-message-text p:last-child {
  margin-bottom: 0;
}

.chat-message-text code {
  font-family: "SF Mono", "Fira Code", monospace;
  color: var(--accent-green);
  font-size: 0.9em;
  background: rgba(0, 0, 0, .75);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}

/* [data-theme="dark"] .chat-message-text code {
  background: rgba(255, 255, 255, 0.1);
}
[data-theme="light"] .chat-message-text code {
  background: rgba(0, 0, 0, 0.06);
} */

.chat-message-text pre {
  background: var(--fill-tertiary-inverse);
  padding: var(--sp-sm);
  border-radius: var(--radius-md);
  overflow-x: auto;
  margin: var(--sp-xs) 0;
}

/* [data-theme="dark"] .chat-message-text pre {
  background: rgba(255, 255, 255, 0.08);
}
[data-theme="light"] .chat-message-text pre {
  background: rgba(0, 0, 0, 0.06);
} */

.chat-message-text pre code {
  background: none;
  padding: 0;
}

.chat-message-text strong { font-weight: 600; }


.chat-message-text ul,
.chat-message-text ol {
  margin: var(--sp-xs) 0;
  padding-left: var(--sp-lg);
}

.chat-message-text ul { list-style-type: disc; }
.chat-message-text ol { list-style-type: decimal; }

.chat-message-text blockquote {
  border-left: 3px solid var(--border-primary);
  padding-left: var(--sp-sm);
  color: var(--text-primary);
  margin: var(--sp-xs) 0;
  background: var(--bg-secondary);
}

.chat-message-text h1,
.chat-message-text h2,
.chat-message-text h3 {
  font-weight: 600;
  margin: var(--sp-xs) 0 var(--sp-3xs);
}
.chat-message-text h1 { font-size: var(--text-lg); }
.chat-message-text h2 { font-size: var(--text-base); }
.chat-message-text h3 { font-size: var(--text-sm); }

/* ── Face stack for group chat headers ── */

.chat-face-stack {
  display: flex;
  flex-direction: row;
}

.chat-face-stack .avatar,
.chat-face-stack .avatar-img {
  border: 2px solid var(--bg);
  margin-left: -8px;
}

.chat-face-stack .avatar:first-child,
.chat-face-stack .avatar-img:first-child {
  margin-left: 0;
}

.chat-header-ai-avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: var(--fg);
  color: var(--bg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* =============================================================================
   Message action menu
   ============================================================================= */

/* Ellipsis trigger — mirrors the .chat-reaction-trigger layout/reveal pattern */
.chat-message-menu-trigger {
  /* Absolutely positioned: centred on the top-right corner of the bubble */
  position: absolute;
  top: 0;
  right: 0;
  transform: translate(50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm, 6px);
  background: var(--bg-elevated, var(--bg));
  color: var(--text-secondary);
  cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,0.10);
  /* Fade in/out */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease, background 0.15s, color 0.15s;
  z-index: 1;
}
@media (hover: hover) {
  .chat-message-menu-trigger:hover {
  background: var(--bg-hover, var(--bg-secondary));
  color: var(--text-primary);
}
}

/* Show trigger on bubble hover — desktop only. Mobile uses the tap action
   sheet instead, so we leave the trigger permanently hidden there. */
@media (min-width: 769px) {
  .chat-message-row.sent .chat-message-bubble-wrapper:focus-within .chat-message-menu-trigger {
    opacity: 1;
    visibility: visible;
  }
@media (hover: hover) {
  .chat-message-row.sent .chat-message-bubble-wrapper:hover .chat-message-menu-trigger {
    opacity: 1;
    visibility: visible;
  }
}
}

/* Dropdown panel — appears above the bubble, right-aligned by default.
   positionMessageMenu() in dom.mjs adds --flip when the top would clip. */
.chat-message-menu-dropdown {
  position: absolute;
  bottom: calc(100% + 4px);
  right: 0;
  min-width: 184px;
  background: var(--bg-primary);
  border: 1px solid var(--border-secondary);
  border-radius: var(--radius-md);
  box-shadow: 0 4px 20px rgba(0,0,0,0.14);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: var(--sp-2xs, 4px) 0;
  z-index: 200;
  overflow: hidden;
}

/* Flip downward when the message is near the top of the chat container */
.chat-message-menu-dropdown--flip {
  bottom: auto;
  top: calc(100% + 4px);
}

/* Bubble-wrapper is the positioning context for the trigger + dropdown */
.chat-message-bubble-wrapper {
  position: relative;
}

.chat-message-menu-item {
  display: flex;
  border-radius: 0;
  align-items: center;
  gap: var(--sp-sm, 10px);
  width: 100%;
  padding: var(--sp-xs, 8px) var(--sp-sm, 12px);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-sm, 0.875rem);
  text-align: left;
  cursor: pointer;
  transition: background 0.2s ease-in-out;
}
@media (hover: hover) {
  .chat-message-menu-item:hover {
  background: var(--bg-secondary);
}
}
.chat-message-menu-item--danger {
  color: var(--text-danger, #e53e3e);
}
@media (hover: hover) {
  .chat-message-menu-item--danger:hover {
  background: var(--bg-danger-hover, rgba(229, 62, 62, 0.08));
}
}

.chat-message-menu-divider {
  height: 1px;
  background: var(--border-primary);
  margin: var(--sp-2xs, 4px) 0;
}

/* =============================================================================
   Emoji-only bubble
   ============================================================================= */

.chat-message-bubble--emoji-only {
  background: transparent;
  border: none;
  padding: 0;
  box-shadow: none;
}

.chat-message-bubble--emoji-only .chat-message-text,
.chat-message-bubble--emoji-only .chat-message-text p {
  font-size: 32px;
  line-height: 1.2;
}

/* =============================================================================
   Inline edit state
   ============================================================================= */

/* ── Full-width editing on desktop ─────────────────────────────────────────
   When a sent message enters edit mode, expand the row to the full readable
   column width so the textarea has maximum writing space.
   Scoped to desktop — mobile edit state uses the composer + cancel bar.
   ─────────────────────────────────────────────────────────────────────────── */
@media (min-width: 769px) {
  /* Expand the row itself — override max-width: 70% and align-self: flex-end */
  .chat-message-row.sent:has(.chat-message-bubble--editing) {
    max-width: 100%;
    width: 100%;
    align-self: stretch;
  }

  /* Stack grows to fill the row width minus the 28px avatar slot */
  .chat-message-row.sent:has(.chat-message-bubble--editing) .chat-message-stack {
    flex: 1;
    min-width: 0;
    align-items: stretch;
  }

  /* Bubble fills the stack; override the pre-measured min-width inline style */
  .chat-message-row.sent:has(.chat-message-bubble--editing) .chat-message-bubble--editing {
    width: 100%;
    min-width: 0 !important;
  }
}

.chat-message-bubble--editing {
  /* min-width is stamped as an inline style (pre-measured in dom.mjs) to
     match the original message row's width, preventing the editing bubble
     from collapsing narrower than the message being edited. The editor fills
     it via width: 100% and can grow beyond min-width for longer edits. */
  padding: 8px;
  overflow: visible;
  background: transparent;
  box-shadow: none;
  box-sizing: border-box;
}

/* Inline message-edit editor — Milkdown inside the editing bubble.
   Shares the .ProseMirror typography/block rules with the composer (see
   the "Milkdown composer" section below); this block is just the frame. */
.chat-edit-milkdown {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 2.5em;
  max-height: 40vh;
  overflow-y: auto;
  padding: var(--sp-xs, 8px) var(--sp-sm, 12px);
  border: 1.5px solid var(--border-focus, var(--color-brand));
  border-radius: var(--radius-md, 10px);
  background: var(--bg);
  color: var(--text-primary);
  font-size: var(--text-sm, 0.875rem);
  line-height: 1.5;
  cursor: text;
  outline: none;
}

/* Attachment strip — sits snugly below the textarea */
.chat-edit-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-xs, 8px);
  margin-top: var(--sp-xs, 8px);
  padding: 0 var(--sp-xs, 8px);
}

/* Individual thumbnail wrapper — relative so the remove btn can be absolute */
.chat-edit-att-thumb {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm, 6px);
  overflow: visible; /* let the button bleed outside */
  max-width: 120px;
}

/* Image / video-poster thumbnail */
.chat-edit-att-thumb-img {
  display: block;
  max-width: 120px;
  max-height: 120px;
  width: auto;
  height: auto;
  border-radius: var(--radius-sm, 6px);
  object-fit: cover;
}

/* Placeholder for non-visual types (file, link without image, embed) */
.chat-edit-att-thumb-placeholder {
  display: flex;
  align-items: center;
  gap: var(--sp-2xs, 4px);
  padding: var(--sp-xs, 8px);
  background: var(--bg-secondary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-sm, 6px);
  font-size: var(--text-xs, 0.8rem);
  color: var(--text-secondary);
  max-width: 120px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ✕ remove button — absolutely positioned at top-left, half outside */
.chat-edit-att-remove {
  position: absolute;
  top: -8px;
  left: -8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full, 999px);
  border: 1.5px solid var(--border-primary);
  background: var(--bg-elevated, var(--bg));
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0;
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
  transition: background 0.15s, color 0.15s;
}
@media (hover: hover) {
  .chat-edit-att-remove:hover {
  background: var(--text-danger, #e53e3e);
  color: #fff;
  border-color: var(--text-danger, #e53e3e);
}
}

.chat-edit-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-xs, 8px);
  padding: var(--sp-2xs, 4px) var(--sp-xs, 8px) var(--sp-xs, 8px);
}

.chat-edit-cancel,
.chat-edit-save {
  padding: var(--sp-2xs, 4px) var(--sp-sm, 12px);
  border-radius: var(--radius-sm, 6px);
  font-size: var(--text-xs, 0.8rem);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border-primary);
  transition: background 0.1s;
}
.chat-edit-cancel {
  background: transparent;
  color: var(--text-secondary);
}
@media (hover: hover) {
  .chat-edit-cancel:hover {
  background: var(--bg-hover, var(--bg-secondary));
}
}
.chat-edit-save {
  background: var(--bg-tertiary);
  color: var(--text-secondary);
}
@media (hover: hover) {
  .chat-edit-save:hover {
  background: var(--bg-primary-inverse);
  color: var(--text-primary-inverse);
}
}

/* ── Deleted message tombstone ── */
.chat-message-deleted {
  padding: var(--sp-xs) 0;
}

.chat-message-deleted-text {
  padding: var(--sp-xs) var(--sp-md);
  font-size: var(--text-base);
  font-style: italic;
  color: var(--text-tertiary);
  margin: 0;
  pointer-events: none;
  user-select: none;
}

/* ── (edited) label ── */
.chat-message-edited-label {
  display: block;
  font-size: 0.75rem;
  font-style: italic;
  font-weight: 400;
  color: var(--text-tertiary);
  margin-top: 2px;
  padding-left: 2px;
}

/* =============================================================================
   Delete confirmation modal
   ============================================================================= */

.chat-delete-confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 500;
}

.chat-delete-confirm-modal {
  background: var(--bg-elevated, var(--bg));
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg, 14px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.24);
  padding: var(--sp-lg, 24px);
  max-width: 360px;
  width: calc(100vw - 32px);
}

.chat-delete-confirm-header {
  font-size: var(--text-base, 1rem);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: var(--sp-sm, 10px);
}

/* Preview container — scrollable to handle tall images */
.chat-delete-confirm-preview {
  background: var(--bg-secondary, var(--bg));
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-sm, 6px);
  padding: var(--sp-xs, 8px) var(--sp-sm, 12px);
  margin-bottom: var(--sp-md, 16px);
  max-height: 260px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs, 8px);
}

/* Text portion */
.chat-delete-confirm-preview-text {
  font-size: var(--text-sm, 0.875rem);
  color: var(--text-secondary);
  line-height: 1.5;
}

/* Attachment list */
.chat-delete-confirm-preview-attachments {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs, 8px);
}

/* Base for every attachment type in the modal */
.chat-delete-att-preview {
  display: flex;
  align-items: center;
  gap: var(--sp-xs, 8px);
  font-size: var(--text-sm, 0.875rem);
  color: var(--text-secondary);
}

/* Image */
.chat-delete-att-image {
  display: block;
}
.chat-delete-att-img {
  display: block;
  max-width: 100%;
  max-height: 180px;
  width: auto;
  height: auto;
  border-radius: var(--radius-sm, 6px);
  object-fit: cover;
}

/* Video — poster thumbnail or fallback label */
.chat-delete-att-video-label {
  display: flex;
  align-items: center;
  gap: var(--sp-xs, 8px);
  color: var(--text-tertiary);
}

/* File */
.chat-delete-att-file-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Link */
.chat-delete-att-link {
  gap: var(--sp-sm, 10px);
}
.chat-delete-att-link-img {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border-radius: var(--radius-xs, 4px);
  flex-shrink: 0;
}
.chat-delete-att-link-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Feed content */
.chat-delete-att-feed {
  gap: var(--sp-sm, 10px);
}
.chat-delete-att-feed-thumb {
  width: 48px;
  height: 48px;
  object-fit: cover;
  border-radius: var(--radius-xs, 4px);
  flex-shrink: 0;
}
.chat-delete-att-feed-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Video embed */
.chat-delete-att-embed-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-delete-confirm-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-sm, 10px);
}

.chat-delete-confirm-cancel,
.chat-delete-confirm-delete {
  padding: var(--sp-xs, 8px) var(--sp-md, 16px);
  border-radius: var(--radius-sm, 6px);
  font-size: var(--text-sm, 0.875rem);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border-primary);
  transition: background 0.1s, opacity 0.1s;
}
.chat-delete-confirm-cancel {
  background: transparent;
  color: var(--text-secondary);
}
@media (hover: hover) {
  .chat-delete-confirm-cancel:hover {
  background: var(--bg-hover, var(--bg-secondary));
}
}
.chat-delete-confirm-delete {
  background: var(--text-danger, #e53e3e);
  color: #fff;
  border-color: var(--text-danger, #e53e3e);
}
@media (hover: hover) {
  .chat-delete-confirm-delete:hover {
  opacity: 0.88;
}
}

/* =============================================================================
   Success / info toast
   ============================================================================= */

.chat-toast {
  position: fixed;
  bottom: var(--sp-xl, 24px);
  left: 50%;
  transform: translateX(-50%);
  padding: var(--sp-xs, 8px) var(--sp-md, 16px);
  background: var(--bg-elevated, var(--bg-primary));
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full, 999px);
  font-size: var(--text-sm, 0.875rem);
  white-space: nowrap;
  pointer-events: none;
  z-index: 200;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  animation: chat-toast-in 0.18s ease-out, chat-toast-out 0.3s ease-in 2.2s forwards;
}

/* ============================================================================
   Milkdown rich-text composer
   Milkdown mounts into .chat-composer-milkdown and injects .milkdown >
   .ProseMirror as the actual contenteditable surface.  We style those classes
   to match the previous textarea's look: transparent fill, same font, same
   padding, same scroll cap.
   ========================================================================= */

/* Mount point — flex item that stretches to fill the pill */
.chat-composer-milkdown {
  flex: 1;
  min-width: 0;
  min-height: 32px;
  cursor: text;
  outline: none; /* suppress native focus ring on the wrapper div */
}

/* Before ProseMirror has mounted (.milkdown child is absent), show a
   placeholder so the composer looks like a text input. Once the editor
   lands it fills the div and this pseudo-element disappears. */
.chat-composer-milkdown:not(:has(.milkdown))::before,
.chat-edit-milkdown:not(:has(.milkdown))::before {
  content: attr(data-placeholder);
  display: block;
  font-size: var(--text-sm);
  line-height: 20px;
  padding: 6px 0;
  color: var(--text-tertiary);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Milkdown's outer wrapper div */
.chat-composer-milkdown .milkdown,
.chat-edit-milkdown .milkdown {
  all: unset;
  display: block;
  width: 100%;
}

/* ProseMirror's contenteditable div — the actual editable surface */
.chat-composer-milkdown .ProseMirror,
.chat-edit-milkdown .ProseMirror {
  outline: none;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 20px;
  padding: 6px 0;
  margin: 0;
  min-height: 32px;
  max-height: 250px;
  overflow-y: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Empty-state placeholder shown once Milkdown is mounted but the doc is
   empty. The `is-editor-empty` class is applied by our emptyPlaceholderPlugin
   decoration (milkdown_editor.mjs) whenever the doc has a single empty
   paragraph. We use float+height:0 (the Tiptap convention) so the trailing
   <br class="ProseMirror-trailingBreak"> ProseMirror inserts doesn't push
   the pseudo-element onto a second line. */
.chat-composer-milkdown .ProseMirror p.is-editor-empty:first-child::before,
.chat-edit-milkdown .ProseMirror p.is-editor-empty:first-child::before,
.chat-composer-milkdown .ProseMirror:empty::before,
.chat-edit-milkdown .ProseMirror:empty::before {
  content: "Write a message...";
  float: left;
  height: 0;
  color: var(--text-tertiary);
  pointer-events: none;
}

/* ProseMirror wraps bare text in <p> tags — strip the default margin */
.chat-composer-milkdown .ProseMirror p,
.chat-edit-milkdown .ProseMirror p {
  margin: 0;
}

/* Inline formatting */
.chat-composer-milkdown .ProseMirror strong { font-weight: 700; }
.chat-composer-milkdown .ProseMirror em     { font-style: italic; }

.chat-composer-milkdown .ProseMirror code,
.chat-edit-milkdown .ProseMirror code {
  font-family: "SF Mono", "Fira Code", monospace;
  color: var(--accent-green);
  font-size: 0.9em;
  background: rgba(0, 0, 0, .75);
  padding: 1px 4px;
  border-radius: var(--radius-sm);
}

.chat-composer-milkdown .ProseMirror a,
.chat-edit-milkdown .ProseMirror a {
  color: var(--accent-blue);
  text-decoration: underline;
}

/* Lists */
.chat-composer-milkdown .ProseMirror ul,
.chat-edit-milkdown .ProseMirror ul,
.chat-composer-milkdown .ProseMirror ol,
.chat-edit-milkdown .ProseMirror ol {
  margin: 2px 0;
  padding-left: 1.4em;
}
/* list-style re-enables must cover BOTH editor surfaces: base.css resets
   ul/ol to list-style none, and the edit bubble's editor was missing from
   these three rules — lists in chat-message-bubble--editing rendered
   without ::marker while the composer showed them fine. */
.chat-composer-milkdown .ProseMirror ul,
.chat-edit-milkdown .ProseMirror ul { list-style-type: disc; }
.chat-composer-milkdown .ProseMirror ol,
.chat-edit-milkdown .ProseMirror ol { list-style-type: decimal; }
.chat-composer-milkdown .ProseMirror li,
.chat-edit-milkdown .ProseMirror li { margin: 0; }

/* Block-level code fence */
.chat-composer-milkdown .ProseMirror pre,
.chat-edit-milkdown .ProseMirror pre {
  background: var(--bg-secondary);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  overflow-x: auto;
  font-family: "SF Mono", "Fira Code", monospace;
  font-size: 0.9em;
  margin: 4px 0;
}
.chat-composer-milkdown .ProseMirror pre code,
.chat-edit-milkdown .ProseMirror pre code {
  background: none;
  padding: 0;
}

/* Blockquote */
.chat-composer-milkdown .ProseMirror blockquote,
.chat-edit-milkdown .ProseMirror blockquote {
  border-left: 3px solid var(--border-primary);
  padding-left: var(--sp-sm);
  color: var(--text-primary);
  margin: var(--sp-xs) 0;
  background: var(--bg-secondary);
}

/* Headings — keep them modest in a chat context */
.chat-composer-milkdown .ProseMirror h1,
.chat-edit-milkdown .ProseMirror h1,
.chat-composer-milkdown .ProseMirror h2,
.chat-edit-milkdown .ProseMirror h2,
.chat-composer-milkdown .ProseMirror h3,
.chat-edit-milkdown .ProseMirror h3 {
  font-weight: 700;
  margin: 2px 0;
  line-height: 1.3;
}

/* ProseMirror selection highlight */
.chat-composer-milkdown .ProseMirror ::selection,
.chat-edit-milkdown .ProseMirror ::selection {
  background: rgba(var(--color-blue), .3);
  background-blend-mode: var(--blend-mode);
}

/* ─── Reaction detail sheet content ─────────────────────────────────── */

/* Emoji tab bar */
.reaction-sheet__tabs {
  display: flex;
  gap: 12px; /* 12px between tabs */
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  padding-top: var(--sp-sm);
}
.reaction-sheet__tabs::-webkit-scrollbar {
  display: none;
}

.reaction-sheet__tab {
  display: inline-flex;
  align-items: center;
  gap: 2px; /* 2px between emoji and count */
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-sm); /* 8px */
  border: none;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.1s ease;
}

.reaction-sheet__tab.active {
  background: var(--bg-secondary);
}

.reaction-sheet__tab-emoji {
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

.reaction-sheet__tab-count {
  font-size: var(--text-lg); /* 16px */
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* User list */
.reaction-sheet__users {
  margin-top: 24px; /* 24px gap from tab bar */
  display: flex;
  flex-direction: column;
  gap: 16px; /* 16px between user rows */
}

.reaction-sheet__user {
  display: flex;
  align-items: center;
  gap: 8px; /* 8px between avatar and name */
}

.reaction-sheet__user-name {
  font-size: var(--text-base); /* 14px */
  color: var(--text-primary);
  font-weight: 400;
}

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes share-chat-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

@keyframes typing-bounce {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-3px); }
}

@keyframes chat-toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

@keyframes chat-toast-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (max-width: 768px) {
  /* Back-link ↔ sidebar-toggle swap is handled by the shared
     .page-header-desktop-only / .page-header-sidebar-toggle pins
     (shell.css). */

  .chat-composer-form .emoji-picker {
    width: 100%;
    right: 0;
  }

  .chat-composer-input {
    font-size: var(--text-lg);
  }

  .chat-composer-milkdown .ProseMirror,
  .chat-edit-milkdown .ProseMirror {
    font-size: var(--text-lg);
  }

  .chat-scroll-to-bottom {
    bottom: 142px;
  }
}

@media (max-width: 600px) {
  .chat-embed-collapsible {
    max-width: 320px;
  }
    .chat-message-row {
    max-width: 90%;
  }
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .chat-message-text code {
    background: rgba(255, 255, 255, 0.1);
  }
  :root:not([data-theme]) .chat-message-text pre {
    background: rgba(255, 255, 255, 0.08);
  }
}

/* =============================================================================
   MOBILE MESSAGE ACTION SHEET
   ============================================================================= */

/* Override the default slide-up-sheet sizing for the action sheet — it
   should be auto-height rather than the fixed 70vh used by the reaction sheet. */
.msg-action-sheet {
  height: auto;
  min-height: unset;
  padding-bottom: env(safe-area-inset-bottom, 16px);
  padding-left: 8px !important;
  padding-right: 8px !important;
}

/* ── Content wrapper ──────────────────────────────────────────────────────── */
.msg-action-sheet .slide-up-sheet__content {
  padding: var(--sp-md) var(--sp-sm);
  /* Needed so the absolutely-positioned picker panel clips to this box */
  position: relative;
  overflow: hidden;
}

/* When the sheet is in emoji-picker mode its content becomes a flex column
   so the picker can fill all remaining height below the header.
   NOTE: use double-class selector (.msg-action-sheet.msg-action-sheet--picker)
   for specificity (0,2,0) so it beats .slide-up-sheet { height: 70vh } from
   sheet.css, which is imported later in index.css and would otherwise win the
   same-specificity cascade. */
.msg-action-sheet.msg-action-sheet--picker {
  height: 85vh;
  min-height: unset;
}

.msg-action-sheet--picker .slide-up-sheet__content {
  flex: 1;
  min-height: 0;
  padding-bottom: env(safe-area-inset-bottom, 16px);
}

/* ── Panel transitions (menu ↔ emoji picker) ──────────────────────────────── */
/* Both panels share the same transition so the swap is a smooth
   directional crossfade: normal slides out left while picker slides in
   from the right, and the reverse on the way back. */

.msg-action-sheet__panel {
  transition: opacity 230ms ease, transform 230ms ease;
}

/* Normal panel: visible by default */
.msg-action-sheet__panel--normal {
  opacity: 1;
  transform: translateX(0);
}
.msg-action-sheet--picker .msg-action-sheet__panel--normal {
  opacity: 0;
  transform: translateX(-28px);
  pointer-events: none;
}

/* Picker panel: hidden by default, absolutely fills the content box
   so it doesn't contribute to the sheet's auto-height in normal mode */
.msg-action-sheet__panel--picker {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  opacity: 0;
  transform: translateX(28px);
  pointer-events: none;
}
.msg-action-sheet--picker .msg-action-sheet__panel--picker {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

/* ── "Quick reactions" header ─────────────────────────────────────────────── */
.msg-action-sheet__reactions-header {
  display: block;
  font-size: var(--text-lg);      /* 16px */
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: var(--sp-sm);    /* 12px gap to emoji row */
}

/* ── Quick emoji row ──────────────────────────────────────────────────────── */
.msg-action-sheet__quick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-xxl);   /* 32px gap to menu card */
}

.msg-action-sheet__quick-emoji {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  transition: background 0.12s ease;
}

.msg-action-sheet__quick-emoji:active {
  background: var(--bg-secondary);
}

.msg-action-sheet__quick-more {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  transition: background 0.12s ease;
}

.msg-action-sheet__quick-more > .icon {
  height: 24px;
  width: 24px;
}

.msg-action-sheet__quick-more:active {
  background: var(--bg-secondary);
}

/* ── Menu card ────────────────────────────────────────────────────────────── */
.msg-action-sheet__menu {
  background: var(--bg-body);
  border-radius: var(--radius-lg);  /* 16px */
  padding: var(--sp-sm);            /* 12px all-around */
  display: flex;
  flex-direction: column;
  gap: var(--sp-xxl);               /* 32px between items */
}

/* ── Menu items ───────────────────────────────────────────────────────────── */
.msg-action-sheet__item {
  display: flex;
  align-items: center;
  gap: var(--sp-md);                /* 16px icon → label */
  background: none;
  border: none;
  padding: var(--sp-3xs) 0;         /* 4px vertical padding */
  cursor: pointer;
  text-align: left;
  color: var(--text-secondary);     /* icon color */
  width: 100%;
}

.msg-action-sheet__item .icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.msg-action-sheet__item span {
  font-size: var(--size-md);        /* 16px */
  color: var(--text-primary);
  font-weight: 400;
  line-height: 1.2;
}

/* Delete action — both icon and label are accent-red */
.msg-action-sheet__item--danger,
.msg-action-sheet__item--danger span {
  color: var(--accent-red);
}

/* ── In-sheet emoji picker header (back button + title) ──────────────────── */
.msg-action-sheet__picker-header {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) 0 var(--sp-md);
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--text-primary);
}

.msg-action-sheet__picker-back {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 4px;
  cursor: pointer;
  color: var(--text-secondary);
  border-radius: var(--radius-full);
  flex-shrink: 0;
}
.msg-action-sheet__picker-back:active { opacity: 0.6; }

/* Override the floating-picker defaults when embedded inside the sheet */
.msg-action-sheet .emoji-picker,
.composer-emoji-sheet .emoji-picker {
  position: static;
  width: 100%;
  flex: 1;
  min-height: 0;
  max-height: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  animation: none;
}

/* ── Composer emoji sheet (mobile) ──────────────────────────────────────────
   Slide-up sheet used instead of the floating popover on mobile.
   Reuses .slide-up-sheet base styles; overrides height to fill most of the
   screen so the full emoji grid is comfortably browsable.
   ─────────────────────────────────────────────────────────────────────────── */
.composer-emoji-sheet.slide-up-sheet {
  height: 85vh;
  min-height: unset;
  padding-bottom: env(safe-area-inset-bottom, 16px);
}

.composer-emoji-sheet .slide-up-sheet__content {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* ── Mobile edit cancel bar ────────────────────────────────────────────────
   Spans the full viewport width between the messages list and the composer.
   Appears only on mobile when a message is being edited (rendered by
   view_mobile_edit_cancel_bar; collapses to nothing when element.none()).
   ─────────────────────────────────────────────────────────────────────── */
.chat-cancel-edit__mobile {
  box-shadow: 0px 2px 4px rgba(0, 0, 0, .08);
  background: var(--bg-tertiary);
  grid-row: 3;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-md);           /* 16px between icon and label */
  width: 100%;
  height: var(--size-3xl);     /* 40px total height */
  padding: 0 var(--sp-xl);
  background: var(--bg-body);
  border: none;
  border-radius: 0;
  color: var(--text-secondary);
  font-size: var(--text-lg);   /* 16px */
  cursor: pointer;
  box-sizing: border-box;
}

.chat-cancel-edit__mobile svg {
  width: var(--size-xl);       /* 24px */
  height: var(--size-xl);
  flex-shrink: 0;
}

.chat-cancel-edit__mobile:active {
  opacity: 0.7;
}

/* "Editing message" tiny-time label variant */
.chat-message-tiny-time--editing {
  color: var(--accent-brand, var(--accent));
  font-style: italic;
}

/* ── Mention autocomplete picker ─────────────────────────────────────── */
.chat-mention-picker {
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow);
  max-height: 240px;
  overflow-y: auto;
  margin-bottom: 8px;
}

.chat-mention-picker-item {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md);
  cursor: pointer;
}

@media (hover: hover) {
  .chat-mention-picker-item:hover {
  background: var(--bg-subtle);
}
}

.chat-mention-picker-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.chat-mention-picker-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-mention-picker-username {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-mention {
  color: var(--accent-blue);
  text-decoration: none;
}
@media (hover: hover) {
  .chat-mention:hover {
  text-decoration: underline;
}
}

/* ── Messages list (/chats, no thread open) ──────────────────────────────
   Shown on both mobile and desktop — desktop still has the sidebar's own
   chat list, but /chats now gets a real page instead of a blank
   placeholder (iOS parity mock).

   STRUCTURE PARITY: this whole block mirrors the Activity page's
   structural system (styles/notifications.css) — container geometry,
   header sizing, chip strip, body gaps, row shell and type scale.
   notifications.css is the source of truth; when a structural value
   changes there, change the mirrored value here too. Each rule notes the
   notifications.css rule it mirrors. */

/* Page root — mirrors .notifications-page exactly: no gap/padding of its
   own; .page-header owns the 20px top offset, .chat-list owns the
   header→body spacing via its top padding, and the 54vw/720px column
   constraint lives on .chat-list. */
.chat-list-root {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
  width: 100%;
  overflow: hidden;
  background: var(--bg-primary);
}

@media (min-width: 768px) {
  .chat-list {
    width: 54vw !important;
    max-width: 720px;
  }
}

/* Header is the standardized ui/page_header component (.page-header,
   shell.css) — centered title at all widths; the back arrow is pinned
   mobile-only via .page-header-mobile-only at the call site. The header
   carries its own 16/24 inset, so the root's horizontal padding moved to
   .chat-list-body below. */

/* Chips + scrollable rows wrapper — mirrors .notifications-body. */
/* Column-constrained wrapper — mirrors .notifications exactly. */
.chat-list {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  padding: var(--sp-xs) var(--sp-xl) 0;
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
}

/* Chips + scrollable rows — mirrors .notifications-body exactly. */
.chat-list-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  gap: var(--sp-md);
}

/* Filter chip strip — mirrors .notif-filter-chips. */
.chat-list-filters {
  display: flex;
  flex-direction: row;
  gap: var(--sp-sm);
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  flex-shrink: 0;
}

.chat-list-filters::-webkit-scrollbar {
  display: none;
}

/* Mirrors .notif-chip (padding, radius, type, hover/active treatment). */
.chat-list-filter {
  flex-shrink: 0;
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-full);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.1s, color 0.1s;
}
@media (hover: hover) {
  .chat-list-filter:hover {
  background: var(--bg-secondary);
  color: var(--text-primary);
}
}
.chat-list-filter-active {
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-weight: 500;
}

/* Mirrors .notifications-empty. */
.chat-list-empty {
  padding: var(--sp-xxl) var(--sp-md);
  text-align: center;
  color: var(--text-tertiary);
}

/* Scrollable list wrapper — mirrors .notifications-list-wrap. */
.chat-list-rows {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: clip;
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE/Edge */
}
.chat-list-rows::-webkit-scrollbar {
  display: none; /* Chrome, Safari, Opera */
}

/* Row shell — mirrors .notification (same padding, gap, hover).
   position: relative anchors the absolute unread dot in the left-padding
   zone, exactly like .notification-badge on Activity rows. */
.chat-list-row {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  padding: var(--sp-md) var(--sp-sm);
  position: relative;
  transition: background 0.1s;
  box-sizing: border-box;
  max-width: 100%;
  text-decoration: none;
  color: inherit;
  border-radius: var(--radius-sm);
}
@media (hover: hover) {
  .chat-list-row:hover {
  background: var(--bg-subtle);
  text-decoration: none;
}
}

/* Avatar slot — mirrors .notification-media's square (82px desktop /
   52px mobile) so Messages rows land at exactly the same height as
   Activity rows. The avatar helper inlines a fixed px size, so the
   container drives the real size via !important — same trick as
   .notification-media .avatar. */
.chat-list-avatar {
  position: relative;
  width: 40px;
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
}

.chat-list-avatar .avatar,
.chat-list-avatar .avatar-img {
  width: 100% !important;
  height: 100% !important;
}

/* AI chat rows: brand glyph in a neutral circle — mirrors
   .notification-media-icon-fallback, fills the avatar slot. */
.chat-list-ai-avatar {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-primary);
}

/* Glyphs in the avatar slot render at size 0 (no inline width/height) and
   are sized here — mirrors .notification-media .icon. */
.chat-list-avatar .icon {
  width: 32px;
  height: 32px;
}

/* Mirrors .notification-body (column stack, --sp-3xs gap). */
.chat-list-row-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

/* Title + timestamp share one row — mirrors iOS ChatListRow's
   HStack { Text(name); Spacer(); Text(timeLabel) }: title on the leading
   edge, timestamp pushed to the trailing edge. `min-width: 0` on the title
   (not this row) is what lets the title ellipsize instead of the row
   overflowing — the timestamp is fixed-width content and never shrinks. */
.chat-list-row-top {
  display: flex;
  align-items: baseline;
  gap: var(--sp-sm);
}

/* Mirrors .notification-title. */
.chat-list-row-title {
  flex: 1;
  min-width: 0;
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Subtle scan-ability flourish on top of the existing unread dot — bumps to
   bold rather than changing color/size, so it doesn't compete visually with
   the dot as the primary unread signal. */
.chat-list-row-title--unread {
  font-weight: 700;
}

/* Timestamp — always muted/tertiary regardless of read state, matching
   iOS's `.footnoteStyle()` + `ColorSystem.textTertiary` (never bolded or
   darkened for unread, unlike the title/preview below). */
.chat-list-row-timestamp {
  flex-shrink: 0;
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--text-tertiary);
}

/* Last-message preview line, below the title+timestamp row — mirrors iOS's
   `messagePreview` (single line, lineLimit(1) there / ellipsis here). Only
   rendered by chat.gleam when `last_message_text` is non-empty, mirroring
   iOS's `if !lastMessage.isEmpty`. */
.chat-list-row-subtitle {
  max-width: 80%;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* "@sender: " prefix on the preview line — always tertiary/muted regardless
   of read state, mirroring iOS's `ColorSystem.textTertiary` on the sender
   substring (the message text after it still follows the row's normal
   read/unread color via .chat-list-row-subtitle). */
.chat-list-row-sender {
  font-size: var(--text-base);
  color: var(--text-primary);
}

.chat-list-row-msg {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* Unread dot — mirrors .notification-badge/-dot geometry: an 8px dot
   centered in the 16px zone at the row's left edge (inside the left
   padding), vertically centered. Rendered only when unread; since it is
   absolutely positioned, layout never shifts either way. */
.chat-list-row-unread-dot {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--accent-red);
}

/* ── Mobile overrides (≤ 768px) — mirrors the .notifications block ──── */

@media (max-width: 768px) {
  /* Mirrors .notifications mobile gap; horizontal inset lives on the
     header (own padding) and .chat-list-body, same as desktop. */
  .chat-list-root {
    padding: 0;
    gap: var(--sp-lg);
  }
  .chat-list {
    padding: 0 var(--sp-md);
  }

  /* Mirrors .notification mobile row padding/gap. */
  .chat-list-row {
    padding: 16px 12px 16px 16px;
    gap: var(--sp-sm);
  }

  /* Mirrors .notification-media mobile square — aspect-ratio and the
     100%-sized children from the base rules handle the rest. */
  .chat-list-avatar {
    width: 52px;
  }

  .chat-list-row-unread-dot {
    left: 4px;
  }
}

/* =============================================================================
   Received-message hover pill — [Add reaction | hairline | ⋮]
   ============================================================================= */

/* Split variant: the container keeps the pill chrome (position, border,
   reveal transitions from .chat-reaction-trigger); the inner segments own
   their padding and hover states so the hairline reads as a true divider. */
.chat-reaction-trigger--split {
  gap: 0;
  padding: 0;
  overflow: hidden;
}

/* Neutralise the whole-pill hover tint — hover feedback lives per-segment. */
@media (hover: hover) {
  .chat-reaction-trigger--split:hover {
  background: var(--bg-primary);
  color: var(--text-secondary);
}
}

.chat-reaction-trigger__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-sm);
  border: none;
  background: var(--bg-primary);
  color: inherit;
  cursor: pointer;
  border-radius: 0;
  white-space: nowrap;
  transition: .3s ease all;
}

@media (hover: hover) {
  .chat-reaction-trigger__btn:hover {
  background: var(--bg-secondary);
  color: var(--text-primary);
}
}

.chat-reaction-trigger__divider {
  height: 24px;
  width: 1px;
  background: var(--border-secondary);
}

/* ── Offline / delivery states ─────────────────────────────────────────────
   Chat is server-only: there is no local store behind it, so offline the
   surface can show what was last cached but cannot send, react, or receive.
   These make that legible instead of leaving a live-looking UI that silently
   never updates. */

.chat-offline-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-xs);
    padding: var(--sp-xs) var(--sp-sm);

    background: var(--fill-elevated-secondary);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: 18px;
}

/* An in-flight message is dimmed — it hasn't landed yet, and shouldn't read
   as delivered.

   Scoped to `.sent` (the viewer's own outgoing messages) on purpose. The AI
   streaming placeholder is also modelled as `Sending` — it has no server id
   yet either, which correctly keeps reactions off it — but it is an INCOMING
   message being typed out, not an unsent one. Dimming it would fade every AI
   response while it streams and snap it to full opacity at the end. */
.chat-message-row.sent.chat-message-row--sending .chat-message-bubble-wrapper {
    opacity: 0.6;
}

/* A failed one is dimmed further and paired with the notice below. Left
   in place deliberately: silently deleting what someone typed because the
   network blinked would be worse than showing that it didn't send. */
.chat-message-row--failed .chat-message-bubble-wrapper {
    opacity: 0.5;
}

.chat-message-failed {
    display: flex;
    align-items: center;
    gap: var(--sp-3xs);
    margin-top: var(--sp-4xs);

    color: var(--accent-red);
    font-size: var(--text-xs);
    line-height: 16px;
}

/* Sent messages are right-aligned, and a failed send is always the viewer's
   own — so the notice hugs the same edge as the bubble it belongs to. */
.chat-message-row.sent .chat-message-failed {
    justify-content: flex-end;
}

.chat-message-failed-retry {
    padding: 0;
    border: none;
    background: none;

    color: var(--accent-blue);
    font: inherit;
    font-size: var(--text-xs);
    font-weight: 600;
    cursor: pointer;
}

@media (hover: hover) {
    .chat-message-failed-retry:hover {
      text-decoration: underline;
  }
}

/* ============================================================================
   Chat Detail — settings/info page for a single chat thread.

   Layout mirrors the chat module: full-height column, max-width centered.
   ========================================================================= */

/* ── Layout ──────────────────────────────────────────────────────────── */

.chat-detail {
  display: flex;
  flex-direction: column;
  height: 100vh;   /* fallback */
  height: 100dvh;  /* dynamic viewport */
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
  padding: 0 var(--sp-xl);
}

/* ── Header ──────────────────────────────────────────────────────────── */

.chat-detail-header {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: 0 0 var(--sp-sm) 0;
  border-bottom: 1px solid var(--border-secondary);
}

.chat-detail-back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  color: var(--text-tertiary);
  text-decoration: none;
  font-size: var(--text-sm);
  padding: var(--sp-3xs) var(--sp-xs);
  border-radius: var(--radius-md);
}
@media (hover: hover) {
  .chat-detail-back:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}
}

.chat-detail-heading {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

/* ── Avatar Section ──────────────────────────────────────────────────── */

.chat-detail-avatar-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--sp-xl) 0;
}

.chat-detail-avatar-wrapper {
  position: relative;
  cursor: pointer;
  border-radius: var(--radius-full);
  /* No overflow: hidden — .chat-detail-avatar-badge is absolutely
     positioned at the wrapper's bottom-right corner and needs to render
     outside the circular avatar's own bounds; hidden overflow clipped it. */
}

.chat-detail-avatar-img {
  width: 80px;
  height: 80px;
  border-radius: var(--radius-full);
  object-fit: cover;
  display: block;
}

.chat-detail-avatar-badge {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-xs);
  border: 2px solid var(--bg);
}

.chat-detail-avatar-loading {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--bg-tertiary);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--fill-elevated-secondary);
}

.chat-detail-avatar-file {
  display: none;
}

/* ── Name Section ────────────────────────────────────────────────────── */

.chat-detail-name-section {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) 0;
}

.chat-detail-name {
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.chat-detail-name-edit {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-tertiary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-base);
  line-height: 1;
}
@media (hover: hover) {
  .chat-detail-name-edit:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}
}

.chat-detail-name-editing {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-xs) 0 var(--sp-md);
}

.chat-detail-name-input {
  padding: var(--sp-xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  outline: none;
  text-align: center;
  width: 240px;
  max-width: 100%;
}
.chat-detail-name-input:focus {
  border-color: var(--bg-secondary);
}

.chat-detail-name-actions {
  display: flex;
  gap: var(--sp-xs);
}

.chat-detail-name-actions button {
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  border: 1px solid var(--border-primary);
  background: var(--bg);
  color: var(--text-primary);
}
@media (hover: hover) {
  .chat-detail-name-actions button:hover {
  background: var(--bg-tertiary);
}
}

/* ── Members Section ─────────────────────────────────────────────────── */

.chat-detail-members {
  padding: var(--sp-md) 0;
}

.chat-detail-members-header {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding-bottom: var(--sp-sm);
}

.chat-detail-sheet .chat-detail-members-header  .title {
  font-size: var(--text-lg);
  color: var(--text-primary);
}

.chat-detail-member-count {
  font-size: var(--text-base);
  color: var(--text-secondary);
  font-weight: 500;
}

.chat-detail-add-member {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-lg);
  line-height: 1;
}
@media (hover: hover) {
  .chat-detail-add-member:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}
}

.chat-detail-member-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.chat-detail-member-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-md);
}
@media (hover: hover) {
  .chat-detail-member-row:hover {
  background: var(--bg-tertiary);
}
}

.chat-detail-member-info,
.chat-detail-member-link {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  text-decoration: none;
  color: inherit;
  min-width: 0;
}
@media (hover: hover) {
  .chat-detail-member-info:hover,
  .chat-detail-member-link:hover {
  text-decoration: none;
}
}

.chat-detail-member-name {
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--accent-blue);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.chat-detail-member-row.you .chat-detail-member-name {
  color: var(--text-primary);
  pointer-events: none;
}


.chat-detail-member-you, .chat-detail-member-row.owner .chat-detail-member-role {
    font-size: var(--text-sm);
    font-weight: 600;
    padding: var(--sp-xs) var(--sp-sm);
    border-radius: var(--radius-full);
    white-space: nowrap;
    flex-shrink: 0;
}

.chat-detail-member-you {
  color: var(--accent-blue);
  background: rgba(var(--color-blue), .1);
  background-blend-mode: var(--blend-mode);
}

.chat-detail-member-row.owner .chat-detail-member-role {
  color: var(--text-primary);
  background: rgba(var(--color-gray-2), .1);
  background-blend-mode: var(--blend-mode);
}

.chat-detail-remove-member {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  line-height: 1;
}
@media (hover: hover) {
  .chat-detail-remove-member:hover {
  background: var(--bg-tertiary);
  color: var(--accent-red);
}
}

.chat-detail-sheet .chat-detail-member-row {
  padding-left: 0;
}

/* ── Actions ─────────────────────────────────────────────────────────── */

/* (Sheet actions live in .slide-up-sheet__footer > .sheet-footer__actions
   with the semantic .button.destructive / .button.danger variants —
   see styles/button.css and sheet.css.) */

/* ── Confirm Dialog ──────────────────────────────────────────────────── */

.chat-detail-confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Must layer above the settings sheet, which sits at the sheet tier
     (401 — see sheet.css desktop block). */
  z-index: 410;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  /* Rendered as a sibling of the settings sheet inside
     `.slide-up-sheet-shell`, which sets `pointer-events: none`. Without
     re-enabling here, clicks fall through to the sheet beneath and the
     Confirm/Cancel buttons aren't even hoverable. (Same fix as
     `.new-chat-overlay` in commit 30a8050.) */
  pointer-events: auto;
}

.chat-detail-confirm-dialog {
  background: var(--bg-secondary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: var(--sp-xl);
  width: min(400px, calc(100vw - 48px));
}

.chat-detail-confirm-dialog p {
  margin: 0 0 var(--sp-lg);
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.5;
}

.chat-detail-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-xs);
}

.chat-detail-confirm-cancel {
  padding: var(--sp-xs) var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}
@media (hover: hover) {
  .chat-detail-confirm-cancel:hover {
  background: var(--bg-tertiary);
}
}

.chat-detail-confirm-confirm {
  padding: var(--sp-xs) var(--sp-md);
  border: none;
  border-radius: var(--radius-md);
  background: var(--accent-red);
  color: #ffffff;
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}
@media (hover: hover) {
  .chat-detail-confirm-confirm:hover {
  opacity: 0.9;
}
}

/* ── Sheet chrome ────────────────────────────────────────────────────── */

/* Chat Settings renders in the shared adaptive slide-up-sheet chrome
   (sheet.css): bottom sheet ≤768px with drag-to-dismiss (attach_sheet_drag
   on #chat-detail-sheet), centered modal ≥769px. Only chat-specific
   sizing and inner layout live here. */

/* Desktop modal: keep the old .chat-detail-modal footprint (480px wide,
   capped height) instead of sheet.css's 560px Medium default. The
   two-class selector outspecifies sheet.css's single-class desktop width
   rule regardless of import order. */
@media (min-width: 769px) {
  .slide-up-sheet.chat-detail-sheet {
    width: min(480px, 92vw);
    max-height: min(640px, 80vh);
  }
}

/* Header row pinned above the scrollable body. */
.chat-detail-sheet .chat-detail-header {
  flex-shrink: 0;
}

/* Scrollable body — .slide-up-sheet__content supplies flex/overflow and
   the sheet panel supplies the horizontal inset (--sp-md); this adds the
   inner breathing room and hides the scrollbar like the old modal body. */
.chat-detail-body {
  padding: 0 var(--sp-xs) var(--sp-xl);
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.chat-detail-body::-webkit-scrollbar {
  display: none;
}

/* Close button (X) in modal header. */
.chat-detail-close {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-tertiary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  margin-left: auto;
}
@media (hover: hover) {
  .chat-detail-close:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}
}

/* Member-count badge inside the sheet. */

.chat-detail-member-role {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  flex-shrink: 0;
}

.chat-detail-member-remove {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  padding: var(--sp-3xs);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  flex-shrink: 0;
}
@media (hover: hover) {
  .chat-detail-member-remove:hover {
  background: var(--bg-tertiary);
  color: var(--accent-red);
}
}

.chat-detail-members-section {
  padding: var(--sp-md) 0;
}

.chat-detail-members-header {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding-bottom: var(--sp-sm);
}

.chat-detail-members-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.chat-detail-name-save,
.chat-detail-name-cancel {
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
  border: 1px solid var(--border-primary);
  background: var(--bg);
  color: var(--text-primary);
}
@media (hover: hover) {
  .chat-detail-name-save:hover,
  .chat-detail-name-cancel:hover {
  background: var(--bg-tertiary);
}
}

/* The thread-header settings gear is a plain page_header.icon_button
   (.header-icon-btn, shell.css); the former .chat-header-actions /
   .chat-header-detail-link chrome was retired with the ui/page_header
   migration. */

/* Notifications feature styles. */

/* ── Mobile header (hidden on desktop) ───────────────────────────────── */
/* ── Layout ──────────────────────────────────────────────────────────── */

/* Body column under the standardized full-width page header
   (.notifications-page hosts both; header is NOT bound to this column). */
.notifications-page {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-secondary);
}

.notifications {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  padding: var(--sp-xs) var(--sp-xl) 0;
  width: 100%;
  margin: 0 auto;
  overflow: hidden;
}

@media (min-width: 768px) {
  .notifications {
    width: 54vw;
    max-width: 720px;
  }
}

/* .notifications-menu-btn sizing/color/radius/hover come from
   .header-icon-btn. */

/* ── Notifications body (chips + scrollable list, used when Loaded) ─── */

.notifications-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  gap: var(--sp-md);
}

/* ── Filter chip strip ───────────────────────────────────────────────── */

.notif-filter-chips {
  display: flex;
  flex-direction: row;
  gap: var(--sp-sm);
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  flex-shrink: 0;
}

.notif-filter-chips::-webkit-scrollbar {
  display: none;
}

.notif-chip {
  flex-shrink: 0;
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-full);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.1s, color 0.1s;
}

.notif-chip:hover {
  background: var(--bg-secondary);
  color: var(--text-primary);
}

.notif-chip--active {
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-weight: 500;
}

/* ── Scrollable list wrapper ─────────────────────────────────────────── */

.notifications-list-wrap {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: clip; /* clip in-flow horizontal overflow without creating a scroll
                       container on x — so absolutely positioned row buttons and
                       popovers are not clipped the way overflow:hidden would do */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.notifications-list-wrap::-webkit-scrollbar {
  display: none;
}

/* ── Date-group sections container ───────────────────────────────────── */

.notifications-sections {
  display: flex;
  flex-direction: column;
}

/* ── Sections ───────────────────────────────────────────────────────── */

.notifications-section {
  display: flex;
  flex-direction: column;
}

.notifications-section + .notifications-section {
  margin-top: var(--sp-xl);
}

/* Clickable row that holds the heading + chevron */
.notifications-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: none;
  border: none;
  padding: 0 0 var(--sp-xs) 0;
  cursor: pointer;
  color: inherit;
  width: 100%;
}

.notifications-section-heading {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
}

/* Chevron */
.notifications-section-header > svg {
  color: var(--text-secondary);
  flex-shrink: 0;
  transition: transform 0.25s ease;
}

/* ── Collapsible section body ────────────────────────────────────────── */

/* The grid row trick: animate between 1fr (natural height) and 0fr.
   overflow:clip clips in-flow content for the collapse animation just like
   overflow:hidden, but unlike hidden it does NOT create a scroll container —
   so absolutely positioned descendants (row popovers) escape it freely. */
.notifications-section-body {
  display: grid;
  grid-template-rows: 1fr;
  /* minmax(0, 1fr) caps the implicit column at the container width.
     Without this the track defaults to minmax(auto, auto) which lets
     overflowing child content expand the column beyond its parent. */
  grid-template-columns: minmax(0, 1fr);
  overflow: clip;
  transition: grid-template-rows 0.25s ease;
}

.notifications-section-body--collapsed {
  grid-template-rows: 0fr;
}

/* Direct child must have min-height: 0 for the 0fr row to actually
   collapse to zero (otherwise the browser respects the min-content size). */
.notifications-section-body > .notifications-list {
  min-height: 0;
}

.notifications-list {
  display: flex;
  flex-direction: column;
  overflow-x: clip; /* prevent rows from bleeding outside the list on narrow viewports */
}

/* ── Row ────────────────────────────────────────────────────────────── */

/* Notification row: position:relative so the badge can be absolutely
   placed in the left-padding zone. Padding is intentionally asymmetric:
   12px left leaves exactly enough room for the 16px badge container. */
.notification {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  padding: var(--sp-md) var(--sp-sm) var(--sp-md) var(--sp-xl);
  position: relative;
  transition: background 0.1s;
  box-sizing: border-box;
  max-width: 100%;
}

.notification:hover {
  background: var(--fill-secondary);
}

.notification:hover > * {
  text-decoration: none !important;
}

/* ── Badge ── */

/* 16px hit area sits in the 12px left-padding zone.  Dot is hidden by
   default; .notification-unread makes it red. Layout never shifts. */
.notification-badge {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.notification-badge-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: transparent;
}

.notification-unread .notification-badge-dot {
  background: var(--accent-red);
}

/* ── Media ── */

/* 1:1 media square (iOS parity: the Activity row's fixed square slot that
   media fills edge-to-edge). Positioning context for the kind badge. */
.notification-media {
  position: relative;
  width: 82px;
  aspect-ratio: 1 / 1;
  flex-shrink: 0;
}

/* Feed item thumbnail (server-provided art) — cover-crops the square. */
.notification-media-thumb {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: var(--radius-md);
  object-fit: cover;
}

/* Shared per-category thumb (ui/media_thumb.gleam) fills the square. */
.notification-media .media-thumb {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-md);
}

/* Actor avatars fill the square too (the avatar helper inlines a fixed
   82px, so !important lets the container drive the size on mobile). */
.notification-media .avatar,
.notification-media .avatar-img {
  width: 100% !important;
  height: 100% !important;
}

/* Playful alternating tilt on media art (iOS rotates its media slot a
   constant −8°; the web keeps its odd/even flourish). */
.notification:nth-child(odd) .notification-media-thumb,
.notification:nth-child(odd) .notification-media .media-thumb {
  transform: rotate(-2deg);
}

.notification:nth-child(even) .notification-media-thumb,
.notification:nth-child(even) .notification-media .media-thumb {
  transform: rotate(2deg);
}

/* Icon fallback when no art is available — neutral circle + kind glyph
   (iOS parity: the reminder bell / AI star fallback circle). */
.notification-media-icon-fallback {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

/* Nested glyphs: sized here (not inline) so contexts can restyle them.
   Icons in the media slot render at size 0 (no inline width/height). */
.notification-media .icon {
  width: 32px;
  height: 32px;
}

/* The corner kind-badge keeps its small glyph, exempt from the 32px rule. */
.notification-media-badge .icon {
  width: 14px;
  height: 14px;
}

/* Kind badge pinned past the bottom-right corner of the media square
   (iOS parity: NotificationAvatarView's 16px badge with a +4px offset on
   a 42px tile — scaled to the web's 82px square). */
.notification-media-badge {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--fill-secondary);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

/* ── Body (the <a> wrapping time + title + text) ── */

.notification-body {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  /* <a> resets */
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

/* In select mode the body is a div, not an anchor — same layout. */
.notification-body--div {
  pointer-events: none; /* the outer div handles clicks in select mode */
}

.notification-time {
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notification-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.notification-text {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Menu wrap ── */

/* Always the last flex child — flex-shrink: 0 guarantees it's never
   squeezed off screen. position: relative anchors the desktop popover. */
.notification-row-menu-wrap {
  position: relative;
  flex-shrink: 0;
}

.notification-menu-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: var(--sp-xs);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
}

.notification-menu-btn:hover {
  background: var(--bg-secondary);
}

/* ── Desktop popover (hidden on mobile via media query) ── */

.notification-row-popover-wrap {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
}

.notification-row-popover {
  min-width: 180px;
  background: var(--bg-elevated, var(--bg-primary));
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Danger variant for the Delete item */
.notifications-popover-item--danger {
  color: var(--accent-red, #e5534b);
}

.notifications-popover-item--danger:hover {
  background: color-mix(in srgb, var(--accent-red, #e5534b) 10%, transparent);
}

/* ── Mobile row action sheet (hidden on desktop) ── */

/* The backdrop and sheet are position:fixed so they escape the row's
   stacking context. Both are display:none on desktop and shown only in
   the mobile media query below.
   Two-class selectors (.slide-up-sheet-backdrop.notif-sheet-backdrop and
   .slide-up-sheet.notif-action-sheet) have higher specificity than the
   single-class rules in sheet.css, so they win regardless of load order. */
.slide-up-sheet-backdrop.notif-sheet-backdrop,
.slide-up-sheet.notif-action-sheet {
  display: none;
}

/* Auto-height sheet (no fixed 70vh like the reaction sheet). */
.notif-action-sheet {
  height: auto;
  min-height: unset;
  padding-bottom: env(safe-area-inset-bottom, 16px);
}

.notif-action-sheet .slide-up-sheet__content {
  padding: var(--sp-md) var(--sp-sm);
  display: flex;
  flex-direction: column;
}

.notif-sheet-item {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: var(--sp-sm) var(--sp-lg);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.notif-sheet-item:hover {
  background: var(--bg-secondary);
}

.notif-sheet-item--danger {
  color: var(--accent-red, #e5534b);
}

.notif-sheet-item--danger:hover {
  background: color-mix(in srgb, var(--accent-red, #e5534b) 10%, transparent);
}

/* ── Toast ───────────────────────────────────────────────────────────── */

.notifications-toast {
  position: fixed;
  bottom: var(--sp-xl, 24px);
  left: 50%;
  transform: translateX(-50%);
  padding: var(--sp-xs, 8px) var(--sp-md, 16px);
  background: var(--bg-elevated, var(--bg-primary));
  color: var(--text-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full, 999px);
  font-size: var(--text-sm, 0.875rem);
  white-space: nowrap;
  pointer-events: none;
  z-index: 200;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  animation: chat-toast-in 0.18s ease-out, chat-toast-out 0.3s ease-in 2.2s forwards;
}

/* ── Popover menu ───────────────────────────────────────────────────── */

/* Wraps the ⋮ button + popover so the popover can be positioned relative
   to this container. */
.notifications-menu-wrap {
  position: relative;
}

/* Full-screen invisible backdrop — closes the menu on outside click. */
.notifications-popover-backdrop {
  position: fixed;
  inset: 0;
  z-index: 10;
}

/* The dark card that floats below the ⋮ button. */
.notifications-popover-wrap {
  position: absolute;
  top: calc(100% + var(--sp-xs));
  right: 0;
  z-index: 11;
}

.notifications-popover {
  position: relative;
  z-index: 11; /* must exceed backdrop's z-index: 10 within the same stacking context */
  background: var(--bg-elevated, var(--bg-secondary));
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle, var(--border-primary));
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  padding: var(--sp-xs) 0;
  min-width: 180px;
  overflow: hidden;
}

.notifications-popover-item {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: var(--sp-sm) var(--sp-lg);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
}

.notifications-popover-item:hover {
  background: var(--bg-secondary);
}

/* ── Per-section select header (replaces chevron header in select mode) */

.notifications-section-select-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--sp-xs);
}

.notifications-select-all {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  user-select: none;
}

.notifications-select-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
}

.notifications-select-action-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  background: none;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  padding: var(--sp-3xs) var(--sp-sm);
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
}

.notifications-select-action-btn:hover:not(:disabled) {
  background: var(--bg-secondary);
}

.notifications-select-action-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

.notifications-select-action-btn--danger {
  color: var(--accent-red);
  border-color: var(--accent-red);
}

.notifications-select-action-btn--danger:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent-red) 10%, transparent);
}

.notifications-select-cancel-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: var(--sp-xs);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
}

.notifications-select-cancel-btn:hover {
  background: var(--bg-secondary);
}

/* ── Select mode rows ───────────────────────────────────────────────── */

/* Card-style wrapper for rows in select mode */
.notification-selectable {
  border-radius: var(--radius-lg);
  margin: 2px var(--sp-xs);
  transition: background 0.1s;
}

.notification-selected {
  background: var(--bg-subtle);
}

/* ── Checkbox ────────────────────────────────────────────────────────── */

.notif-checkbox {
  appearance: none;
  -webkit-appearance: none;
  width: 20px;
  min-width: 20px;
  height: 20px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--border-primary);
  background: var(--bg);
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}

.notif-checkbox:checked {
  background: var(--accent-primary, var(--text-primary));
  border-color: var(--accent-primary, var(--text-primary));
}

.notif-checkbox:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 2px;
  width: 6px;
  height: 10px;
  border: 2px solid var(--bg);
  border-top: none;
  border-left: none;
  transform: rotate(45deg);
}

/* ── Empty / error states ───────────────────────────────────────────── */

.notifications-empty,
.notifications-loading,
.notifications-error {
  padding: var(--sp-xxl) var(--sp-md);
  text-align: center;
  color: var(--text-tertiary);
}

/* ── Load more ──────────────────────────────────────────────────────── */

.notifications-load-more {
  align-self: center;
  margin: var(--sp-md);
  padding: var(--sp-xs) var(--sp-md);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-primary);
  background: var(--bg);
  color: var(--text-primary);
  font-size: var(--text-sm);
  cursor: pointer;
}

.notifications-load-more:hover {
  background: var(--bg-subtle);
}

.notifications-load-more:disabled {
  opacity: 0.6;
  cursor: default;
}

/* ── Sidebar unread badge (used by shell.css) ───────────────────────── */

.sidebar-badge {
  margin-left: auto;
  background: var(--accent-red);
  color: #ffffff;
  border-radius: var(--radius-full);
  padding: var(--sp-3xs) var(--sp-xs);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1;
  min-width: 18px;
  text-align: center;
}

.sidebar-collapsed .sidebar-badge {
  display: none;
}

/* ── Mobile overrides (≤ 768px) ─────────────────────────────────────── */

@media (max-width: 768px) {
  .notifications {
    padding: 0 var(--sp-md);
    gap: var(--sp-lg);
  }

  /* Tighten row padding on narrow screens. */
  .notification {
    padding: 16px 12px 16px 16px;
    gap: var(--sp-sm);
  }

  /* Shrink the media square — aspect-ratio and the 100%-sized children
     from the base rules handle the rest. */
  .notification-media {
    width: 52px;
  }

  /* Scale the kind badge down with the square. */
  .notification-media-badge {
    width: 20px;
    height: 20px;
  }

  /* Too tight for the duration label at 52px — keep just the glyph. */
  .notification-media .media-thumb-voice-duration {
    display: none;
  }

  /* Hide desktop popover; show mobile sheet instead. */
  .notification-row-popover-wrap {
    display: none;
  }

  .slide-up-sheet-backdrop.notif-sheet-backdrop {
    display: block;
  }

  .slide-up-sheet.notif-action-sheet {
    display: flex;
  }
}

/* ---------- Inline follow-request actions ---------- */
/* A flex cluster sitting between the row body and the ⋮ menu. Mirrors the
   followers-list accept/reject button styling. */
.notification-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  flex-shrink: 0;
  margin-right: var(--sp-xs);
}

.notif-action-btn {
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-family: inherit;
  cursor: pointer;
  white-space: nowrap;
}

.notif-action-btn:disabled {
  opacity: 0.6;
  cursor: default;
}

.notif-action-accept,
.notif-action-follow {
  background: var(--color-primary);
  color: white;
  border: none;
}

.notif-action-deny {
  background: none;
  border: 1px solid var(--border-default);
  color: var(--text-tertiary);
}

.notif-action-label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  white-space: nowrap;
  flex-shrink: 0;
  margin-right: var(--sp-xs);
}

/* Search screen — query input + results grid. Results reuse .feed-grid
   and .feed-card from feed.css. */

/* Page root — mirrors .notifications-page: full-width shell hosting the
   standardized header above the column-constrained body. */
.search-page {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-secondary);
}

.search {
  flex: 1;
  width: 68vw;
  margin: 0 auto;
  width: 68vw;
  max-width: 68vw;
  padding-top: var(--sp-xs);
  padding-left: var(--sp-xs);
  padding-right: var(--sp-xs);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ── Header row: back button + pill input ─────────────────────────────── */

.search-header {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  margin-bottom: var(--sp-xl);
}

.search-input {
  flex: 1;
  height: 48px;
  padding: var(--sp-sm) var(--sp-md);
  border: 1px solid transparent;
  border-radius: 9999px;
  background: var(--bg-primary);
  color: var(--text-primary);
  font: inherit;
  outline: none;
  transition: border-color 120ms ease;
}

.search-input::placeholder {
  color: var(--text-tertiary);
}

.search-input:focus {
  border-color: var(--accent-blue);
}

.feed .search-input {
  color: var(--text-tertiary);
}

/* ── Scrollable body ──────────────────────────────────────────────────── */

.search-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: var(--sp-xl);
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.search-body::-webkit-scrollbar {
  display: none;
}

/* ── Empty / prompt state ─────────────────────────────────────────────── */

.search-prompt {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
  color: var(--text-tertiary);
}

.search-prompt-icon {
  opacity: 0.4;
}

.search-prompt-text {
  margin: 0;
  font-size: var(--text-base);
}

.search-empty,
.search-loading,
.search-error {
  padding: var(--sp-3xl) var(--sp-md);
  text-align: center;
  color: var(--text-tertiary);
}

.search-error {
  color: var(--accent-red);
}

.search-result-count {
  text-align: center;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  padding: var(--sp-md) 0;
}

/* ── Results ──────────────────────────────────────────────────────────────
   Results are reminder rows (ui/timeline_list), same as the reminders list —
   see styles/timeline_row.css for the row itself.

   Deliberately NOT .feed-items-wrap: that is a scroll container, and
   .search-body already is one, so reusing it would nest a second scroller
   inside the first. This is a plain column; .search-body does the scrolling.

   Capped at the feed's column width so a result row is the same size as the
   same row on the reminders list — .search is wider (68vw) than .feed
   (54vw / 900px) because the search input wants the room, but the rows
   shouldn't inherit that. */
.search-results {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);

  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  padding-bottom: var(--sp-lg);
}

.search-results .tl-list {
  width: 100%;
}

/* Monogram avatar — circular badge with a letter on a deterministic
   desaturated background. Sizes are set inline via the `avatar()` helper
   so one CSS class supports arbitrary px sizes. */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  flex-shrink: 0;
  font-weight: 600;
  font-family: var(--font-sans);
  line-height: 1;
  color: #ffffff;
  user-select: none;
  letter-spacing: 0;
  text-transform: uppercase;
}

/* Muted/desaturated palette — picked so they sit quietly next to the
   monochrome icon set. Tuned for both light and dark modes. */
.avatar-color-1 { background: #8b7aa8; } /* dusty violet */
.avatar-color-2 { background: #7a9a8e; } /* sage */
.avatar-color-3 { background: #b58863; } /* terracotta */
.avatar-color-4 { background: #6e8caf; } /* slate blue */
.avatar-color-5 { background: #c08a6a; } /* warm tan */
.avatar-color-6 { background: #8a9a5b; } /* olive */
.avatar-color-7 { background: #a66e7e; } /* mauve */
.avatar-color-8 { background: #5f8a8b; } /* teal */

[data-theme="dark"] .avatar-color-1 { background: #6a5a82; }
[data-theme="dark"] .avatar-color-2 { background: #5a7568; }
[data-theme="dark"] .avatar-color-3 { background: #8a6547; }
[data-theme="dark"] .avatar-color-4 { background: #526b86; }
[data-theme="dark"] .avatar-color-5 { background: #8f6550; }
[data-theme="dark"] .avatar-color-6 { background: #687543; }
[data-theme="dark"] .avatar-color-7 { background: #7f5361; }
[data-theme="dark"] .avatar-color-8 { background: #476668; }

.avatar-img {
  border-radius: var(--radius-full);
  object-fit: cover;
  flex-shrink: 0;
}

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .avatar-color-1 { background: #6a5a82; }
  :root:not([data-theme]) .avatar-color-2 { background: #5a7568; }
  :root:not([data-theme]) .avatar-color-3 { background: #8a6547; }
  :root:not([data-theme]) .avatar-color-4 { background: #526b86; }
  :root:not([data-theme]) .avatar-color-5 { background: #8f6550; }
  :root:not([data-theme]) .avatar-color-6 { background: #687543; }
  :root:not([data-theme]) .avatar-color-7 { background: #7f5361; }
  :root:not([data-theme]) .avatar-color-8 { background: #476668; }
}

.avatar-error {
  position: relative;
  background: var(--color-surface-2);
  color: var(--color-text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  flex-shrink: 0;
}

.avatar-error-badge {
  position: absolute;
  bottom: -1px;
  right: -1px;
  background: var(--accent-red, #e53935);
  color: #fff;
  font-size: 8px;
  font-weight: 700;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Error state + skeleton loaders — shared across all features. */

/* ── Error state ─────────────────────────────────────────────── */

.error-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-3xl) var(--sp-xl);
  text-align: center;
  color: var(--text-tertiary);
}
.error-state .icon { color: var(--accent-red); }
.error-state-title {
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
}
.error-state-message {
  font-size: var(--text-sm);
  max-width: 320px;
  margin: 0;
}

/* ── Skeleton loaders ────────────────────────────────────────── */

.skeleton-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  break-inside: avoid;
  margin-bottom: var(--sp-sm);
}

.skeleton-media {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--bg-subtle);
  border-radius: var(--radius-lg);
  animation: skeleton-pulse 1.5s ease infinite;
}

.skeleton-hero {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--bg-subtle);
  border-radius: var(--radius-lg);
  animation: skeleton-pulse 1.5s ease infinite;
}

.skeleton-line {
  height: 14px;
  background: var(--bg-subtle);
  border-radius: var(--radius-sm);
  animation: skeleton-pulse 1.5s ease infinite;
}

.skeleton-line-title { width: 70%; }
.skeleton-line-short { width: 40%; }
.skeleton-line-flex { flex: 1; }
.skeleton-line-full { width: 100%; }
.skeleton-line-medium { width: 60%; }

.skeleton-circle {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--bg-subtle);
  animation: skeleton-pulse 1.5s ease infinite;
  flex-shrink: 0;
}

.skeleton-row {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) 0;
}

/* ── Boot screen ─────────────────────────────────────────────── */

.boot {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-lg);
  color: var(--text-primary);
}

.boot-spinner {
  display: flex;
  gap: var(--sp-xs);
}

.boot-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--text-tertiary);
  animation: boot-pulse 1.4s ease-in-out infinite both;
}
.boot-dot:nth-child(2) { animation-delay: 0.16s; }
.boot-dot:nth-child(3) { animation-delay: 0.32s; }

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes skeleton-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 0.8; }
}

@keyframes boot-pulse {
  0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
  40% { opacity: 1; transform: scale(1); }
}

/* ── Drop zone ─────────────────────────────────────────────────────────────
   Drag a photo in from the desktop. Applied to whole surfaces (the New
   Reminder page, a reminder's detail page) rather than a small target, so it
   needs a clearer affordance than the chat composer's background tint: a
   dashed inset outline that reads as "this whole thing will take the file".

   The overlay is a ::after so it can't disturb layout, and it's inert to the
   pointer — a drop zone that swallowed its own drop event would be a poor
   sort of drop zone. */

.nr-root.drag-over::after,
.detail.drag-over::after {
  content: "Drop to attach";
  position: fixed;
  inset: var(--sp-sm);
  z-index: 60;

  display: flex;
  align-items: center;
  justify-content: center;

  border: 2px dashed var(--accent-blue);
  border-radius: var(--radius-lg, 14px);
  background: rgba(var(--color-blue), 0.06);
  color: var(--accent-blue);

  font-size: var(--text-lg);
  font-weight: 600;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .nr-root.drag-over::after,
  .detail.drag-over::after {
    animation: drop-zone-in 0.12s ease-out;
  }
}

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

/* Profile page — avatar, stats, follow button, content placeholder. */

.profile {
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  max-width: 720px;
  margin: 0 auto;
  padding: var(--sp-xl) var(--sp-xxl);
  box-sizing: border-box;
}
.profile::-webkit-scrollbar { display: none; }

.profile-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-md);
}

.profile-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-xs);
  padding-bottom: var(--sp-xl);
}

.profile-avatar {
  width: 96px;
  height: 96px;
  border-radius: var(--radius-full);
  background: var(--bg-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  font-weight: 600;
  color: var(--text-tertiary);
}

.profile-name {
  font-size: var(--text-2xl);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
  text-align: center;
}

.profile-username {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  margin: 0;
}

.profile-badges {
  display: flex;
  gap: var(--sp-xs);
  align-items: center;
}

.profile-badge {
  font-size: var(--text-xs);
  padding: 2px var(--sp-xs);
  border-radius: var(--radius-full);
  background: var(--bg-subtle);
  color: var(--text-tertiary);
}

.profile-follow-btn {
  padding: var(--sp-xs) var(--sp-xl);
  border-radius: var(--radius-full);
  border: 1px solid var(--bg-secondary);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
}
.profile-follow-btn:hover { opacity: 0.9; }

.profile-follow-btn-following {
  background: transparent;
  color: var(--text-primary);
  border-color: var(--border-primary);
}
.profile-follow-btn-following:hover {
  border-color: var(--accent-red);
  color: var(--accent-red);
}

.profile-stats {
  display: flex;
  gap: var(--sp-xl);
  justify-content: center;
}

.profile-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.profile-stat-value {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
}

.profile-stat-label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.profile-content-placeholder {
  padding: var(--sp-3xl) var(--sp-xl);
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.profile-loading,
.profile-error {
  padding: var(--sp-xxl);
  text-align: center;
  color: var(--text-tertiary);
}

/* .profile-back and .profile-settings sizing/color/radius/hover come from
   .header-icon-btn. */

.profile-requests-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-xs);
  margin: var(--sp-md) auto;
  width: fit-content;
  padding: var(--sp-xs) var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  background: var(--bg-subtle);
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
}

.profile-requests-btn:hover {
  background: var(--bg-elevated, var(--bg-subtle));
}

.profile-stats {
  margin-bottom: var(--sp-xl);
}

.profile-private {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-3xl) var(--sp-xl);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.profile-mutual-contacts {
  margin: 0;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.profile-blocked {
  margin-top: var(--sp-md);
  padding: var(--sp-md) var(--sp-lg);
  border-radius: var(--radius-md);
  background: var(--surface-secondary);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  text-align: center;
}

.profile-empty {
  padding: var(--sp-3xl) var(--sp-xl);
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

.profile-load-more {
  margin-top: var(--sp-xl);
}

/* ── Followers/Following list ─────────────────────── */

.followers {
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  max-width: 600px;
  margin: 0 auto;
  padding: var(--sp-md);
  box-sizing: border-box;
}
.followers::-webkit-scrollbar { display: none; }

.followers-header {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  margin-bottom: var(--sp-md);
}

.followers-back {
  color: var(--fg-default);
}

.followers-tabs {
  display: flex;
  gap: var(--sp-xs);
  margin-bottom: var(--sp-xl);
}

.followers-tab {
  padding: var(--sp-xs) var(--sp-md);
  background: var(--bg-tertiary);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: normal;
}
.followers-tab-active {
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-weight: 600;
}

.followers-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-lg);
  list-style: none;
  padding: 0;
  margin: 0;
}

.followers-row {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
}

.followers-row-info {
  flex: 1;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}

.followers-row-name {
  font-weight: 500;
  font-size: var(--text-sm);
}

.followers-row-username {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.followers-request-actions {
  display: flex;
  gap: var(--sp-xs);
}

.followers-accept-btn {
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: white;
  border: none;
  font-size: var(--text-xs);
  cursor: pointer;
}

.followers-reject-btn {
  padding: var(--sp-3xs) var(--sp-sm);
  border-radius: var(--radius-full);
  background: none;
  border: 1px solid var(--border-default);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  cursor: pointer;
}

/* Small "x" to remove a follower — own profile, Followers tab only. */
.followers-remove-btn {
  margin-left: auto;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-xs);
  background: none;
  border: none;
  border-radius: var(--radius-full);
  color: var(--text-tertiary);
  cursor: pointer;
}
.followers-remove-btn:hover {
  color: var(--text-primary);
  background: var(--bg-secondary);
}

.followers-empty {
  padding: var(--sp-xxl) 0;
  text-align: center;
  color: var(--text-tertiary);
}

/* Settings page styles */

.settings {
  max-width: 680px;
  margin: 0 auto;
  padding: var(--sp-xl);
}

.settings-header {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  margin-bottom: var(--sp-xl);
}

/* Sidebar toggle is mobile-only in the settings header. */
@media (min-width: 769px) {
  .settings-header .sidebar-collapse-button {
    display: none;
  }
}


.settings-title {
  font-size: var(--text-2xl);
  font-weight: 600;
  margin: 0;
}

/* ── Profile header ─────────────────────────────────────────────────── */

.settings-profile {
  display: flex;
  align-items: center;
  gap: var(--sp-xl); /* 24px */
  margin-bottom: var(--sp-xxl);
}

.settings-profile-avatar {
  position: relative;
  width: 130px;
  height: 130px;
  border-radius: 32px;
  overflow: hidden;
  flex-shrink: 0;
  cursor: default; /* only interactive on mobile */
}

/* Force avatar img/span to fill the clipping container */
.settings-profile-avatar .avatar,
.settings-profile-avatar img.avatar {
  width: 130px !important;
  height: 130px !important;
  border-radius: 0 !important;
  display: block;
}

/* Camera overlay — hidden on desktop, shown on mobile via media query */
.settings-avatar-overlay {
  display: none;
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  align-items: center;
  justify-content: center;
  color: #fff;
  pointer-events: none; /* label handles the click */
}

.settings-profile-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
  min-width: 0;
}

.settings-profile-name {
  font-size: var(--text-4xl); /* 32px */
  font-weight: 500;
  color: var(--text-primary);
  line-height: 1.1;
}

.settings-profile-username {
  font-size: 24px;
  font-weight: 400;
  color: var(--text-secondary);
  line-height: 1.2;
}

.settings-change-photo-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--sp-3xl); /* 40px */
  padding: var(--sp-md) var(--sp-sm); /* 16px 12px */
  background: var(--bg-secondary);
  border: none;
  border-radius: var(--radius-lg);
  font: inherit;
  font-size: var(--text-lg); /* 16px */
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  white-space: nowrap;
}

.settings-change-photo-btn:hover {
  filter: brightness(0.95);
}

.settings-avatar-input-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Form ───────────────────────────────────────────────────────────── */

.settings-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xl); /* 24px */
}

/* ── Row ────────────────────────────────────────────────────────────── */

.settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* gap: var(--sp-md); */
  width: 100%;
}

.settings-row--btn {
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  text-align: left;
  padding: 0;
}

.settings-row-left {
  display: flex;
  align-items: center;
  gap: var(--sp-sm); /* 12px between icon and label */
  flex-shrink: 0;
  width: 172px;
}

.settings-row-icon {
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.settings-row-label {
  font-size: var(--text-lg); /* 16px */
  font-weight: 400;
  color: var(--text-secondary);
}

.settings-row-input {
  flex: 1;
  height: var(--sp-3xl); /* 40px */
  padding: 0 var(--sp-sm);
  background: var(--bg-secondary);
  border: none;
  border-radius: var(--radius-lg);
  font: inherit;
  font-size: var(--text-lg);
  color: var(--text-primary);
  outline: none;
  min-width: 0;
}

.settings-row-input:focus {
  background: var(--bg-tertiary, var(--bg-secondary));
}

.settings-row-input:disabled {
  color: var(--text-tertiary);
  cursor: default;
}

.settings-row-right {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  margin-left: auto;
  color: var(--text-tertiary);
}

.settings-row-value {
  font-size: var(--text-lg);
  font-weight: 400;
  color: var(--text-secondary);
}

/* ── Privacy / visibility pill toggle ───────────────────────────────── */

.settings-privacy-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  background: transparent;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 0;
  cursor: pointer;
  font: inherit;
  transition: background 200ms ease, border-color 200ms ease;
}

/* Disabled (non-interactive) state */
.settings-privacy-toggle:disabled {
  opacity: 0.45;
  cursor: default;
}

/* On state (right / public) */
.settings-privacy-toggle--public {
  background: var(--bg-secondary);
  border-color: transparent;
}

.settings-privacy-toggle--public .settings-privacy-indicator {
  background: var(--bg);
}

/* Sliding indicator — sits behind the option labels (z-index 0).
   Default position: left half (Private active).
   Squish: when arriving at a side, the leading edge moves first and the
   trailing edge follows with a short delay, creating a stretch-then-snap
   feel. Each state declares the transition for arriving at that state. */
.settings-privacy-indicator {
  position: absolute;
  top: 3px;
  bottom: 3px;
  left: 3px;
  right: 50%;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.06));
  border-radius: var(--radius-full);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.10);
  z-index: 0;
  /* Arriving at Private (left): left edge leads, right follows */
  transition:
    left  240ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
    right 240ms cubic-bezier(0.4, 0, 0.2, 1) 50ms;
}

/* Public active: slide indicator to right half */
.settings-privacy-toggle--public .settings-privacy-indicator {
  left: 50%;
  right: 3px;
  /* Arriving at Public (right): right edge leads, left follows */
  transition:
    right 240ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
    left  240ms cubic-bezier(0.4, 0, 0.2, 1) 50ms;
}

.settings-privacy-opt {
  position: relative;
  z-index: 1;
  flex: 1;
  padding: 5px 14px;
  border: none;
  background: transparent;
  border-radius: var(--radius-full);
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: color 180ms ease, font-weight 180ms ease;
}

.settings-privacy-opt--active {
  font-weight: 600;
  color: var(--text-primary);
}

/* ── Error ──────────────────────────────────────────────────────────── */

.settings-error {
  padding: var(--sp-sm);
  margin-bottom: var(--sp-md);
  background: var(--danger-subtle, rgba(220, 38, 38, 0.08));
  color: var(--accent-red);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

/* ── Push notifications row ─────────────────────────────────────────── */

.settings-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-xs) 0;
}

.settings-toggle-label {
  font-size: var(--text-lg);
  font-weight: 400;
  color: var(--text-secondary);
}

.settings-push-status,
.settings-push-denied,
.settings-push-error {
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.settings-push-denied,
.settings-push-error {
  color: var(--accent-red);
}

.settings-push-btn,
.settings-push-disable-btn {
  padding: var(--sp-3xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}

.settings-push-btn:hover,
.settings-push-disable-btn:hover {
  background: var(--bg-subtle);
}

.settings-push-active {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  font-size: var(--text-sm);
  color: var(--text-primary);
}

/* ── Save button ────────────────────────────────────────────────────── */

.settings-save-btn {
  align-self: flex-start;
  padding: var(--sp-xs) var(--sp-md);
  border: none;
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  color: var(--text-secondary);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.settings-save-btn:hover {
  filter: brightness(0.95);
}

.settings-save-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* ── Divider ────────────────────────────────────────────────────────── */

.settings-divider {
  border: none;
  border-top: 1px solid var(--border-primary);
  margin: var(--sp-md) 0;
}

/* ── Telegram link row ──────────────────────────────────────────────── */

.settings-link-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-sm) var(--sp-xs);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 500;
}

.settings-link-row:hover {
  background: var(--bg-subtle);
  text-decoration: none;
}

.settings-link-label {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
}

/* ── Sign-out ───────────────────────────────────────────────────────── */

.settings-signout-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xs) var(--sp-md);
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-tertiary);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.settings-signout-btn:hover {
  color: var(--accent-red);
  background: var(--danger-subtle, rgba(220, 38, 38, 0.08));
}

/* ─── Mobile overrides (≤ 768px) ────────────────────────────────────── */

@media (max-width: 768px) {
  /* Shrink avatar, switch to radius-md, show camera overlay */
  .settings-profile-avatar {
    width: 72px;
    height: 72px;
    border-radius: var(--radius-md);
    cursor: pointer;
  }

  .settings-profile-avatar .avatar,
  .settings-profile-avatar img.avatar {
    width: 72px !important;
    height: 72px !important;
  }

  /* Show the camera overlay so users know it's tappable */
  .settings-avatar-overlay {
    display: flex;
  }

  /* Larger name down to 2xl, username to 16px */
  .settings-profile-name {
    font-size: var(--text-2xl); /* 22px */
  }

  .settings-profile-username {
    font-size: 16px;
  }

  /* Hide the separate "Change Photo" button — avatar tap handles it */
  .settings-change-photo-btn {
    display: none;
  }
}

/* ── Notes folder (vault location, desktop only) ───────────────────────────
   The vault is plain Markdown and it's the source of truth, so the user can
   keep it wherever they like. The Gleam side renders nothing on web. */

.settings-vault {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  padding: var(--sp-md) 0;
  border-top: 1px solid var(--separator, rgba(120, 120, 128, 0.2));
}

.settings-vault-head {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-sm);
}

.settings-vault-icon {
  display: inline-flex;
  flex: 0 0 auto;
  color: var(--text-secondary);
}

.settings-vault-heading {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4xs);
}

.settings-vault-label {
  font-size: var(--text-lg);
  font-weight: 500;
}

.settings-vault-hint {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 18px;
}

/* The path can be long and must not push the settings column wide — it
   scrolls inside its own box instead. */
.settings-vault-path {
  overflow-x: auto;
  padding: var(--sp-xs) var(--sp-sm);

  border-radius: var(--radius-md, 10px);
  background: var(--fill-elevated-tertiary);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.settings-vault-actions {
  display: flex;
  gap: var(--sp-xs);
}

.settings-vault-btn {
  padding: var(--sp-xs) var(--sp-sm);

  border: none;
  border-radius: var(--radius-full);
  background: var(--fill-elevated-secondary);
  color: var(--text-primary);

  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.settings-vault-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.settings-vault-btn--quiet {
  background: none;
  color: var(--accent-blue);
}

.settings-vault-warning {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 16px;
}

.settings-vault-result {
  margin: 0;
  color: var(--accent-green);
  font-size: var(--text-sm);
  line-height: 18px;
}

.settings-vault-result--error {
  color: var(--accent-red);
}

/* AI Preferences page styles */

.ai-prefs {
  max-width: 560px;
  margin: 0 auto;
  padding: var(--sp-xl);
}

.ai-prefs-title {
  font-size: var(--text-2xl);
  font-weight: 600;
  margin: 0 0 var(--sp-xl) 0;
}

.ai-prefs-error {
  padding: var(--sp-sm);
  margin-bottom: var(--sp-md);
  background: var(--danger-subtle, rgba(220, 38, 38, 0.08));
  color: var(--accent-red);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

.ai-prefs-section {
  margin-bottom: var(--sp-xxl);
}

.ai-prefs-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-sm);
}

.ai-prefs-section-title {
  font-size: var(--text-lg);
  font-weight: 600;
  margin: 0;
  color: var(--text-primary);
}

.ai-prefs-section-description {
  margin: 0 0 var(--sp-sm);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.ai-prefs-add-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: var(--sp-3xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
  color: var(--text-primary);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.ai-prefs-add-btn:hover {
  background: var(--bg-subtle);
}

.ai-prefs-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
}

.ai-prefs-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-sm);
  padding: var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg);
}

.ai-prefs-item-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.ai-prefs-item-intent {
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--text-primary);
  word-wrap: break-word;
}

.ai-prefs-item-meta {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  flex-wrap: wrap;
}

.ai-prefs-item-actions {
  display: flex;
  gap: var(--sp-3xs);
  flex-shrink: 0;
}

.ai-prefs-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
}

.ai-prefs-icon-btn:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}

.ai-prefs-icon-btn-danger:hover {
  background: var(--danger-subtle, rgba(220, 38, 38, 0.08));
  color: var(--accent-red);
}

.ai-prefs-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--sp-xs);
  border-radius: var(--radius-full, 999px);
  background: var(--bg-subtle);
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  font-weight: 500;
}

.ai-prefs-empty {
  padding: var(--sp-md);
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  border: 1px dashed var(--border-primary);
  border-radius: var(--radius-md);
}

/* Form */
.ai-prefs-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
  padding: var(--sp-md);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  background: var(--bg-subtle);
  margin-bottom: var(--sp-sm);
}

.ai-prefs-form-row {
  display: flex;
  gap: var(--sp-sm);
  flex-wrap: wrap;
}

.ai-prefs-form-row > .ai-prefs-field {
  flex: 1;
  min-width: 140px;
}

.ai-prefs-field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3xs);
}

.ai-prefs-field-label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-tertiary);
}

.ai-prefs-field-hint {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  margin-top: calc(-1 * var(--sp-3xs));
}

.ai-prefs-input,
.ai-prefs-select,
.ai-prefs-textarea {
  padding: var(--sp-xs) var(--sp-sm);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-base);
  color: var(--text-primary);
  background: var(--bg);
}

.ai-prefs-input:focus,
.ai-prefs-select:focus,
.ai-prefs-textarea:focus {
  outline: none;
  border-color: var(--bg-secondary);
}

.ai-prefs-textarea {
  min-height: 96px;
  resize: vertical;
}

.ai-prefs-form-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-xs);
}

.ai-prefs-btn {
  padding: var(--sp-xs) var(--sp-md);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
}

.ai-prefs-btn-primary {
  background: var(--bg-secondary);
  color: var(--text-secondary);
}

.ai-prefs-btn-primary:hover {
  opacity: 0.9;
}

.ai-prefs-btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.ai-prefs-btn-secondary {
  background: var(--bg);
  color: var(--text-primary);
  border-color: var(--border-primary);
}

.ai-prefs-btn-secondary:hover {
  background: var(--bg-subtle);
}

/* Processing badge on feed cards */

.feed-card-processing {
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: var(--sp-lg) var(--sp-sm);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.feed-card-processing-spinner {
  width: 12px;
  height: 12px;
  border: 1.5px solid var(--border-primary);
  border-top-color: var(--text-tertiary);
  border-radius: 50%;
  animation: processing-spin 0.8s linear infinite;
}

.feed-card-processing-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--text-tertiary);
  animation: processing-pulse 1.5s ease-in-out infinite;
}

.feed-card-processing-error .feed-card-processing-dot {
  background: var(--accent-red);
  animation: none;
}

.feed-card-processing-error {
  color: var(--accent-red);
}

.feed-card-processing-warning {
  color: #d97706;
}

.feed-card-processing-warning .feed-card-processing-dot {
  background: #d97706;
  animation: none;
}

/* Retry button inside errored processing badge */

.feed-card-retry-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3xs);
  margin-left: auto;
  padding: var(--sp-4xs) var(--sp-xs);
  border: 1px solid currentColor;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--accent-red);
  font-size: var(--text-xs);
  font-family: inherit;
  cursor: pointer;
  line-height: 1;
}

.feed-card-retry-btn:hover {
  opacity: 0.75;
}

.feed-card-retry-btn .icon {
  width: var(--size-sm);
  height: var(--size-sm);
}

/* Pill positioning override for processing/error cards —
   pull the pill out of absolute flow and let the container carry the offset */
.feed-card:has(.feed-card-processing) .feed-badge-pill {
  position: relative;
  top: auto;
  left: auto;
}

.feed-card:has(.feed-card-processing) .feed-card-link {
  padding-top: var(--sp-sm);
  padding-left: var(--sp-sm);
}

/* Processing toast */

.processing-toast {
  position: fixed;
  bottom: var(--sp-xl);
  right: var(--sp-xl);
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: var(--sp-xs);
  width: min(300px, calc(100vw - 2 * var(--sp-xl)));
}

/* Each toast pill */
.processing-toast-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 12px;
  animation: toast-slide-in 0.2s ease-out;
}

/* Top row: [icon+name] on the left, [status] on the right */
.processing-toast-row {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 10px;
}

/* Icon and filename grouped, flex row, gap 8px */
.processing-toast-left {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}

/* Icon container fills parent height, centers the SVG */
.processing-toast-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  color: #ffffff;
  flex-shrink: 0;
}

.processing-toast-name {
  font-size: var(--text-sm);
  font-weight: 500;
  color: #ffffff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
}

.processing-toast-status {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.5);
  min-width: 36px;
  justify-content: flex-end;
}

.processing-toast-done {
  color: #22c55e;
}

.processing-toast-error-text {
  color: #f87171;
}

.processing-toast-spinner {
  width: 10px;
  height: 10px;
  border: 1.5px solid rgba(255, 255, 255, 0.25);
  border-top-color: rgba(255, 255, 255, 0.7);
  border-radius: 50%;
  animation: processing-spin 0.8s linear infinite;
  flex-shrink: 0;
}

/* Progress bar */
.processing-toast-bar {
  height: 3px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.processing-toast-bar-fill {
  height: 100%;
  border-radius: var(--radius-full);
}

.processing-toast-bar--indeterminate .processing-toast-bar-fill {
  width: 40%;
  background: var(--accent-blue);
  animation: processing-indeterminate 1.4s ease-in-out infinite;
}

.processing-toast-bar--done .processing-toast-bar-fill {
  width: 100%;
  background: #22c55e;
  transition: width 0.3s ease;
}

.processing-toast-bar--error .processing-toast-bar-fill {
  width: 100%;
  background: #f87171;
}

/* ─── Keyframe animations ────────────────────────────────────────────── */

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

@keyframes processing-indeterminate {
  0%   { transform: translateX(-100%); }
  50%  { transform: translateX(150%); }
  100% { transform: translateX(150%); }
}

@keyframes processing-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

@keyframes toast-slide-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

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

/* Recommended-users row — horizontally scrollable cards at the top of the
   For You and Following feed tabs. */
.rec-users-section {
  margin-bottom: var(--sp-xl);
  transition: margin-bottom 320ms cubic-bezier(0.4, 0, 0.2, 1);
}

.rec-users-section--hidden {
  margin-bottom: 0;
}

.rec-users-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--sp-sm);
}

.rec-users-title {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-primary);
}

.rec-users-toggle {
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  cursor: pointer;
  font-family: inherit;
  padding: var(--sp-3xs) var(--sp-xs);
  /* Stack both labels in the same grid cell so the button auto-sizes
     to the label width; overflow clips the translateY slide */
  display: inline-grid;
  overflow: hidden;
}
.rec-users-toggle:hover { color: var(--text-primary); }

/* Both labels sit in grid cell 1/1 and slide past each other */
.rec-users-toggle-label {
  grid-row: 1;
  grid-column: 1;
  transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1),
              opacity 180ms ease;
}

/* Section visible → "Hide" centred, "Show" waiting below */
.rec-users-toggle-label--hide {
  transform: translateY(0%);
  opacity: 1;
}
.rec-users-toggle-label--show {
  transform: translateY(100%);
  opacity: 0;
}

/* Section hidden → "Show" rolls into view, "Hide" exits upward */
.rec-users-section--hidden .rec-users-toggle-label--hide {
  transform: translateY(-100%);
  opacity: 0;
}
.rec-users-section--hidden .rec-users-toggle-label--show {
  transform: translateY(0%);
  opacity: 1;
}

/* ── Body collapse / expand ─────────────────────────────────────────── */

.rec-users-body {
  display: grid;
  grid-template-rows: 1fr;
  overflow: hidden;
  opacity: 1;
  transition: grid-template-rows 320ms cubic-bezier(0.4, 0, 0.2, 1),
              opacity 260ms ease;
}

.rec-users-section--hidden .rec-users-body {
  grid-template-rows: 0fr;
  opacity: 0;
}

/* Inner scroll needs min-height: 0 for the grid collapse to work;
   its own overflow-x: auto is unaffected and horizontal scroll still works */
.rec-users-scroll {
  min-height: 0;
  display: flex;
  gap: var(--sp-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  margin-bottom: var(--sp-md);
}
.rec-users-scroll::-webkit-scrollbar { display: none; }

.rec-users-card {
  flex: 0 0 140px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-sm);
  padding: var(--sp-sm);
  border-radius: var(--radius-lg);
  background: var(--bg-subtle);
}

.rec-users-card-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-xs);
  text-decoration: none;
  color: inherit;
}

.rec-users-name {
  font-weight: 500;
  font-size: var(--text-sm);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
}

.rec-users-handle {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rec-users-follow {
  padding: var(--sp-xs) var(--sp-sm);
  border-radius: var(--radius-full);
  border: 1.5px solid var(--fg);
  background: var(--fg);
  color: var(--bg);
  font-size: var(--text-xs);
  font-family: inherit;
  cursor: pointer;
}
.rec-users-follow:hover { opacity: 0.9; }
.rec-users-follow[disabled] { opacity: 0.6; cursor: default; }

.rec-users-follow-following,
.rec-users-follow-requested {
  background: transparent;
  color: var(--text-primary);
}
.rec-users-follow-requested {
  border-color: var(--text-tertiary);
  color: var(--text-tertiary);
}

/* Skeletons */
.rec-users-card-skeleton { gap: var(--sp-xs); align-items: center; }
.rec-users-skeleton-avatar {
  width: 64px; height: 64px; border-radius: 50%;
  background: var(--bg-muted, #e5e5e5);
}
.rec-users-skeleton-line {
  height: 10px; width: 80%; border-radius: var(--radius-sm, 4px);
  background: var(--bg-muted, #e5e5e5);
}
.rec-users-skeleton-line-short { width: 50%; }
.rec-users-skeleton-button {
  margin-top: var(--sp-xs);
  width: 80%; height: 24px; border-radius: var(--radius-full);
  background: var(--bg-muted, #e5e5e5);
}

/* client/styles/emoji_picker.css — scoped to the new chat emoji picker. */

/* Compact pill (hover state on bubbles) */
.emoji-picker-pill {
  position: absolute;
  top: -12px;
  /* Left-anchored so the pill grows RIGHTWARD — the open side for
     received messages (they hug the pane's left). Right-anchoring grew
     it leftward, clipping against the scroll container on bubbles
     narrower than the pill (same inversion the reaction trigger had). */
  left: -4px;
  display: flex;
  align-items: center;
  gap: var(--sp-3xs);
  padding: var(--sp-3xs) var(--sp-xs);
  background: var(--bg);
  border: 1px solid var(--border-subtle, var(--border-primary));
  border-radius: var(--radius-full);
  box-shadow: var(--shadow);
  z-index: 10;
  animation: emoji-picker-pill-open 0.12s ease-out;
}

@keyframes emoji-picker-pill-open {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Sent bubbles hug the pane's right — mirror the anchor so the pill
   grows LEFTWARD into open space. */
.chat-message-row.sent .emoji-picker-pill {
  left: auto;
  right: -4px;
}

.emoji-picker-pill-emoji {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: 18px;
  padding: 0;
  transition: background 0.1s ease, transform 0.1s ease;
}

@media (hover: hover) {
  .emoji-picker-pill-emoji:hover {
  background: var(--bg-subtle);
  transform: scale(1.15);
}
}

.emoji-picker-overflow {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  color: var(--text-tertiary);
  border-radius: var(--radius-full);
  cursor: pointer;
  padding: 0;
}

@media (hover: hover) {
  .emoji-picker-overflow:hover {
  background: var(--bg-subtle);
  color: var(--fg);
}
}

/* Drill-down panel — fixed positioning applied inline by view */
.emoji-picker {
  position: fixed;
  width: 336px;
  max-height: 420px;
  display: flex;
  flex-direction: column;
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  z-index: 100;
  overflow: hidden;
  animation: emoji-picker-open 0.12s ease-out;
}

/* Category strip at top */
.emoji-picker-category-strip {
  display: flex;
  justify-content: space-between;
  padding: var(--sp-sm) var(--sp-md);
  gap: var(--sp-xs);
}

.emoji-picker-category-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-3xs);
  background: none;
  border: none;
  color: var(--text-tertiary);
  cursor: pointer;
  border-radius: var(--radius-full);
}

.emoji-picker-category-btn-active {
  color: var(--text-secondary);
  background: var(--bg-secondary);
}
@media (hover: hover) {
  .emoji-picker-category-btn:hover {
  color: var(--text-secondary);
  background: var(--bg-secondary);
}
}

.emoji-picker-category-btn:focus-visible {
  outline: 2px dashed var(--text-tertiary);
  outline-offset: 2px;
}

/* Search */
.emoji-picker-search-wrap {
  padding: var(--sp-md) var(--sp-sm);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-xs);
}

.emoji-picker-search {
  flex: 1;
  min-width: 0;
  padding: var(--sp-xs) var(--sp-md);
  background: var(--bg-subtle);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  font: inherit;
  font-size: var(--text-lg);
  color: var(--text-secondary);
}

.emoji-picker-search::placeholder {
  color: var(--text-secondary);
}

.emoji-picker-search:focus-visible {
  outline: 2px solid var(--bg-secondary);
  outline-offset: 0;
}

/* Global skin-tone button */
.emoji-picker-tone-global-btn {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  background: none;
  border-radius: var(--radius-sm);
  font-size: 24px;
  cursor: pointer;
  padding: 0;
  color: inherit;
  transition: background 0.1s ease, border-color 0.1s ease;
}

@media (hover: hover) {
  .emoji-picker-tone-global-btn:hover {
  background: var(--bg-subtle);
}
}

.emoji-picker-tone-global-btn--active {
  background: var(--bg-subtle);
  border-color: var(--border-primary);
}
@media (hover: hover) {
  .emoji-picker-tone-global-btn--active:hover {
  background: var(--bg-subtle);
  border-color: var(--border-primary);
}
}

.emoji-picker-tone-global-btn:focus-visible {
  outline: 2px dashed var(--text-tertiary);
  outline-offset: 2px;
}

/* Global tone selector panel — wraps to its own row inside search-wrap */
.emoji-picker-global-tone-panel {
  flex-basis: 100%;
  display: flex;
  gap: var(--sp-3xs);
  padding-top: var(--sp-3xs);
  animation: emoji-picker-tone-open 0.08s ease-out;
}

.emoji-picker-global-tone-swatch {
  flex: 1;
  aspect-ratio: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid transparent;
  background: none;
  border-radius: var(--radius-sm);
  font-size: 20px;
  cursor: pointer;
  padding: 0;
  transition: background 0.1s ease, border-color 0.1s ease;
}

@media (hover: hover) {
  .emoji-picker-global-tone-swatch:hover {
  background: var(--bg-subtle);
}
}

.emoji-picker-global-tone-swatch--active {
  border-color: var(--fg);
}

@media (hover: hover) {
  .emoji-picker-global-tone-swatch--active:hover {
  background: var(--bg-subtle);
}
}

.emoji-picker-global-tone-swatch:focus-visible {
  outline: 2px dashed var(--text-tertiary);
  outline-offset: 2px;
}

/* Scroll region */
.emoji-picker-scroll {
  flex: 1;
  min-height: 0; /* required: without this a flex item won't shrink below its
                    content height, making scrollHeight === clientHeight and
                    disabling programmatic + touch scroll entirely */
  overflow-y: auto;
  scroll-behavior: smooth;
  padding: 0 var(--sp-md) var(--sp-md);
}

.emoji-picker-section {
  padding-top: var(--sp-sm);
}

.emoji-picker-section-header {
  position: sticky;
  top: 0;
  background: var(--bg-primary);
  padding: var(--sp-xs) 0;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-tertiary);
  margin: 0;
}

.emoji-picker-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--sp-3xs);
}

.emoji-picker-cell {
  aspect-ratio: 1 / 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 22px;
  cursor: pointer;
  padding: 0;
}

.emoji-picker-cell:focus-visible {
  background: var(--bg-subtle);
}
@media (hover: hover) {
  .emoji-picker-cell:hover {
  background: var(--bg-subtle);
}
}

.emoji-picker-cell:focus-visible {
  outline: none;
}

/* Empty search state */
.emoji-picker-empty {
  text-align: center;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: var(--sp-xl) var(--sp-md);
}

.emoji-picker-results {
  padding-top: var(--sp-sm);
}

/* Tone popout */
.emoji-picker-tone-popout {
  position: fixed;
  display: flex;
  gap: var(--sp-3xs);
  padding: var(--sp-3xs) var(--sp-xs);
  background: var(--bg-primary);
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow);
  z-index: 110;
  animation: emoji-picker-tone-open 0.08s ease-out;
}

.emoji-picker-tone-btn {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: var(--radius-sm);
  font-size: 20px;
  cursor: pointer;
  padding: 0;
}

@media (hover: hover) {
  .emoji-picker-tone-btn:hover {
  background: var(--bg-subtle);
}
}

/* Reduced motion */

/* ─── Keyframe animations ────────────────────────────────────────────── */

@keyframes emoji-picker-pill-open {
  from { opacity: 0; transform: translateY(-2px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes emoji-picker-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes emoji-picker-tone-open {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ─── GIF tab button ─────────────────────────────────────────────────── */

/* GIF text button sits in the same strip as emoji category buttons */
.emoji-picker-gif-btn {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0 var(--sp-3xs);
  min-width: 36px;
}

/* ─── GIF panel ──────────────────────────────────────────────────────── */

/* 2-column masonry-style grid */
.emoji-picker-gif-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-xs);
  padding: var(--sp-xs);
}

.emoji-picker-gif-cell {
  display: block;
  width: 100%;
  padding: 0;
  border: none;
  background: var(--bg-secondary);
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  transition: opacity 0.1s;
}

@media (hover: hover) {
  .emoji-picker-gif-cell:hover {
  opacity: 0.85;
}
}

.emoji-picker-gif-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-md);
}

/* While a new search is in flight over existing results, keep the grid
   visible but dimmed — swapping to the spinner collapses the panel. */
.emoji-picker-gif-grid-loading {
  opacity: 0.55;
  transition: opacity 0.15s ease-out;
}

/* Loading spinner (first fetch only — no results to keep on screen).
   Fills the panel's scroll region so entering GIF mode doesn't collapse
   the picker to spinner height. */
.emoji-picker-gif-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 300px;
}

.emoji-picker-gif-spinner {
  width: 24px;
  height: 24px;
  border: 2.5px solid var(--border-primary);
  border-top-color: var(--text-secondary);
  border-radius: 50%;
  animation: emoji-picker-gif-spin 0.7s linear infinite;
}

@keyframes emoji-picker-gif-spin {
  to { transform: rotate(360deg); }
}

/* Empty state — same fill-height as the loading state, so "no results"
   doesn't collapse the panel either. */
.emoji-picker-gif-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 300px;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

/* Attribution footer — required by Klipy's terms; the .icon mask inherits
   currentColor, so the mark themes with --text-tertiary. */
.emoji-picker-gif-attribution {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-xs) 0;
  border-top: 1px solid var(--border-primary);
  color: var(--text-tertiary);
}

.emoji-picker-gif-attribution .icon {
  width: 76px;
  height: 13px;
}

/* ─── Media queries ──────────────────────────────────────────────────── */

@media (max-width: 768px) {
  .emoji-picker-cell {
    font-size: var(--text-4xl);
  }
}

@media (prefers-reduced-motion: reduce) {
  .emoji-picker-scroll {
    scroll-behavior: auto !important;
  }

  .emoji-picker,
  .emoji-picker-tone-popout,
  .emoji-picker-global-tone-panel,
  .emoji-picker-pill,
  .emoji-picker-pill-emoji,
  .emoji-picker-cell,
  .emoji-picker-category-btn,
  .emoji-picker-tone-global-btn,
  .emoji-picker-global-tone-swatch,
  .emoji-picker-gif-spinner {
    animation: none !important;
    transition: none !important;
  }
}

/* ─── Slide-up sheet (iOS-style bottom drawer) ───────────────────────────────
   Reusable component for mobile bottom-sheet patterns.

   Usage:
     .slide-up-sheet-shell     — fixed full-viewport-height positioning shell.
                                  Wraps the backdrop + panel pair; see below
                                  for why this extra layer exists.
     .slide-up-sheet-backdrop  — full-screen dim/blur behind the sheet; tapping
                                  it should dispatch a dismiss message.
     .slide-up-sheet           — the sheet panel itself. fit-content height up
                                  to ~95vh, draggable further via JS (down to
                                  dismiss, up to expand — see dom.mjs).
     .slide-up-sheet__handle-bar — drag target at the top of the sheet.
     .slide-up-sheet__content    — scrollable content area inside the sheet.
     .slide-up-sheet__footer     — OPTIONAL pinned action row after the
                                   content. A plain flex sibling (the sheet
                                   is a flex column and __content owns the
                                   scrolling), so it stays put while content
                                   scrolls — the standardized home for any
                                   bottom-aligned sheet/modal actions.

   Markup shape at every call site (chat.gleam, detail.gleam, new_reminder.gleam,
   notifications.gleam, reminder_picker.gleam):
     html.div([class="slide-up-sheet-shell"], [
       html.div([class="slide-up-sheet-backdrop"], []),
       html.div([id=sheetId, class="slide-up-sheet ..."], [
         html.div([class="slide-up-sheet__handle-bar"], []),
         html.div([class="slide-up-sheet__content"], [...]),
         html.div([class="slide-up-sheet__footer"], [          // optional
           html.div([class="sheet-footer__actions"], [...actions]),
         ]),
       ]),
     ])

   Why the shell: `position: fixed; bottom: 0` on the panel directly anchors
   to the LAYOUT viewport, which iOS Safari does NOT shrink when its bottom
   toolbar is expanded — only the VISUAL viewport shrinks. That leaves the
   panel's bottom edge sitting underneath the toolbar, so however many
   pixels of its content render behind/covered by the browser chrome even
   when the panel's own height is correctly capped. The shell sidesteps this
   ambiguity entirely: it's `position: fixed; top: 0` (top-anchoring doesn't
   have this bug) with an explicit `height: var(--vvh)` — a JS-measured exact
   pixel value (dom.mjs, visualViewport.height), not a CSS relative unit — so
   its own box always matches the truly visible area. The panel is then a
   flex child bottom-aligned *within* that correctly-sized box, so it can
   never be positioned lower than what's actually on screen.

   The JS `attachSheetDrag(sheetId, onDismiss)` in dom.mjs manages drag
   behaviour: down past ~40% of the panel's height (or a fast flick) plays
   an exit animation and calls onDismiss; up past ~30% of the room left to
   the 95vh cap (or a fast upward flick) snaps the panel open to that cap.

   Adaptive presentation: on desktop viewports (≥ 769px, the complement of
   `isMobile()` in dom.mjs) the same markup renders as a centered modal that
   matches the ui/modal visual system (styles/modal.css — default overlay
   opacity 0.85 / blur 4px, Medium 560px panel width). The shell is
   neutralized (display: contents) so it doesn't affect this layout at all,
   the handle bar is hidden, the entrance animation is a fade/scale instead
   of the slide-up, and attachSheetDrag skips the drag listeners (see the
   desktop block at the bottom of this file and the media-query gate in
   dom.mjs).
──────────────────────────────────────────────────────────────────────────── */

.slide-up-sheet-shell {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--vvh, 100dvh);
  z-index: 401;
  display: flex;
  align-items: flex-end;
  /* The shell's own box has nothing to click on directly — the backdrop
     (which fills it via absolute inset:0) and the panel opt back in below. */
  pointer-events: none;
}

.slide-up-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  animation: sheet-backdrop-in 0.25s ease forwards;
  pointer-events: auto;
}

.slide-up-sheet {
  /* Flex child of .slide-up-sheet-shell now, not independently fixed — see
     the file-header comment for why. */
  position: relative;
  width: 100%;
  height: fit-content;
  /* Plain `vh` is sized against the browser's LARGEST possible viewport
     (chrome/toolbar hidden), not what's actually visible right now — on a
     real mobile browser with its address/tab bar showing, that overstates
     the available height, so a tall sheet's `fit-content` box can grow
     past the truly visible area and get clipped by the toolbar, cutting
     off content instead of just scrolling internally. `--vvh`
     (visualViewport.height, kept live by dom.mjs) is the shell's own
     "ground truth" fix for this same class of bug — reuse it here so the
     cap always matches the real visible viewport. Falls back to `dvh` if
     --vvh hasn't been set yet (matches shell.css's own fallback).
  */
  max-height: calc(var(--vvh, 100dvh) * 0.95);
  min-height: calc(var(--vvh, 100dvh) * 0.30);
  background: var(--bg-primary);
  border-radius: 20px 20px 0 0;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  /* 16px horizontal padding per spec — uses --sp-md */
  padding: 0 var(--sp-md);
  overflow: hidden;
  color: var(--text-primary);
  animation: sheet-slide-in 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards;
  will-change: height;
}

/* The handle-bar div is the full 48px touch target; the visible pill is
   rendered via ::after so we can keep the hit area large without affecting
   the pill's visual size. overflow: hidden on the sheet clips anything
   outside, so we use a tall div + flex centering instead of negative margins. */
.slide-up-sheet__handle-bar {
  position: relative;
  width: 100%;
  height: 28px;
  flex-shrink: 0;
  cursor: grab;
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 8px;
}

.slide-up-sheet__handle-bar::after {
  content: "";
  display: block;
  width: 36px;
  height: 4px;
  background: var(--border);
  border-radius: var(--radius-full);
  pointer-events: none;
}

.slide-up-sheet__handle-bar:active {
  cursor: grabbing;
}

.slide-up-sheet__content {
  flex: 1;
  overflow-y: auto;
  /* Prevent content scroll from bubbling during drag. */
  overscroll-behavior: contain;
}

/* Pinned action row — non-scrolling flex sibling below __content. Hairline
   separates it once the content scrolls; safe-area inset keeps actions
   above the iOS home indicator. Works unchanged in the desktop modal
   presentation (same flex skeleton). */
.slide-up-sheet__footer {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  /* Scroll-shadow home: while __content is actively scrolling (JS toggles
     --scrolling via attachSheetFooterShadow, trailing-debounced), an
     upward shadow separates the footer from the content sliding beneath
     it — then eases back out at rest. Replaces the old top hairline.
     The footer paints after __content in DOM order, so the shadow draws
     over the scrolled content. */
  box-shadow: 0 -11px 12px -19px rgba(0, 0, 0, 0.0);
  transition: box-shadow .5s ease all;
}

.slide-up-sheet__footer--scrolling {
  box-shadow: 0 -12px 24px -12px rgba(0, 0, 0, 0.22);
}

/* Actions nest here; it owns the inset the footer used to carry (the
   shadow needs the footer's box to span edge-to-edge). Actions sit
   HORIZONTALLY, sharing the row equally with a 12px gap — two-action
   sheets render side by side, a single action fills the width. */
.sheet-footer__actions {
  display: flex;
  align-items: stretch;
  gap: var(--sp-sm);
  padding: var(--sp-md) var(--sp-lg)
    calc(var(--sp-md) + env(safe-area-inset-bottom));
}
.sheet-footer__actions > * {
  flex: 1 1 0;
  min-width: 0;
}

/* ─── Keyframe animations ────────────────────────────────────────────────── */

@keyframes sheet-slide-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0);    }
}

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

/* Desktop entrance: subtle fade + scale (the slide-in stays mobile-only). */
@keyframes sheet-modal-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1);    }
}

/* ─── Desktop (≥ 769px): centered modal presentation ─────────────────────────
   Mirrors the ui/modal system (.mo-backdrop-dark / .mo-panel / .mo-medium in
   modal.css): dark 0.85 overlay with 4px blur, centered panel, radius-lg,
   deep shadow, 560px default width. The drag handle is hidden and dom.mjs
   skips the drag listeners at this width, so dismissal is backdrop-click /
   ESC / explicit close buttons.

   NOTE: .notif-action-sheet stays display:none on desktop (two-class rule in
   notifications.css beats these single-class rules on specificity) — the
   notifications row menu deliberately uses a popover on desktop.
──────────────────────────────────────────────────────────────────────────── */

@media (min-width: 769px) {
  .slide-up-sheet-shell {
    /* Neutralize the mobile positioning shell entirely — its children render
       as if they were direct siblings of whatever contains it, so the
       centered-modal recipe below applies exactly as it did before the
       shell existed. This rewrite is purely a mobile-Safari-toolbar fix. */
    display: contents;
  }

  .slide-up-sheet-backdrop {
    position: fixed;
    inset: 0;
    /* Match modal.css .mo-backdrop-dark with the default_overlay() values
       (--mo: 0.85, --mb: 4px). */
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    /* display:contents on the shell drops its z-index:401 tier, leaving
       these fixed boxes at z-index:auto — so any later-DOM positioned
       element with an explicit z (composer chrome, jump-to-latest,
       scroll-to-bottom) painted above the backdrop and stayed clickable.
       Re-assert the mobile shell's tier explicitly. */
    z-index: 400;
  }

  /* The two-class variant is included so this beats later single-class
     variant rules (e.g. .nr-action-sheet's env() padding-bottom in
     new_reminder.css, imported after this file). */
  .slide-up-sheet,
  .slide-up-sheet.nr-action-sheet {
    position: fixed;
    /* Above its own backdrop (see z-index note there). */
    z-index: 401;
    /* inset:0 + margin:auto centers the fit-content panel on both axes. */
    inset: 0;
    margin: auto;
    width: min(92vw, 560px);
    height: fit-content;
    min-height: unset;
    max-height: 80vh; /* internal scroll stays on .slide-up-sheet__content */
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.4);
    padding: var(--sp-md);
    animation: sheet-modal-in 0.22s ease forwards;
    will-change: auto;
  }

  /* Menu-style sheets (action rows / pickers) read better narrower than
     the 560px Medium default. */
  .slide-up-sheet.nr-action-sheet,
  .slide-up-sheet.vis-sheet,
  .slide-up-sheet.nr-reminder-sheet {
    width: min(92vw, 420px);
  }

  /* Drag affordance is mobile-only. */
  .slide-up-sheet__handle-bar {
    display: none;
  }
}

/* ============================================================
   Modal / overlay system — ui/modal.gleam
   ============================================================
   CSS custom properties for runtime tuning (set via inline style):
     --mo   overlay opacity  (default 0.85)
     --mb   backdrop blur    (default 4px)

   Z-index ladder:
     detail confirm overlays  ~200
     this modal system        900
     chat image lightbox     1000
   ============================================================ */

/* ── root container ──────────────────────────────────────── */

.mo-root {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  /* CSS vars with fallbacks */
  --mo: 0.85;
  --mb: 4px;
}

/* ── backdrop ─────────────────────────────────────────────── */

.mo-backdrop {
  position: absolute;
  inset: 0;
  cursor: pointer;
}

.mo-backdrop-dark {
  background: rgba(0, 0, 0, var(--mo));
  backdrop-filter: blur(var(--mb));
  -webkit-backdrop-filter: blur(var(--mb));
}

/* ── panel ────────────────────────────────────────────────── */

.mo-panel {
  position: relative; /* sits above .mo-backdrop in stacking context */
  background: var(--bg-primary);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.4);
  overflow: hidden;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
}

.mo-fullscreen {
  width: 100vw;
  height: 100vh;
  max-height: 100vh;
  border-radius: 0;
}

.mo-large {
  width: min(90vw, 1200px);
}

.mo-medium {
  width: min(92vw, 560px);
}

.mo-small {
  width: min(92vw, 380px);
}

/* ── close button ─────────────────────────────────────────── */

.mo-close-btn {
  position: absolute;
  top: var(--sp-sm);
  right: var(--sp-sm);
  z-index: 1;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 120ms ease;
}

.mo-close-btn:hover {
  background: rgba(0, 0, 0, 0.55);
}

/* ── image lightbox ───────────────────────────────────────── */

.mo-lightbox {
  background: #000;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  color: rgba(255, 255, 255, 0.5);
  animation: mo-lb-in 0.3s ease-in-out;
}

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

@keyframes mo-lb-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

.mo-lb-header {
  display: flex;
  align-items: center;
  padding: 12px 8px 8px;
  flex-shrink: 0;
}

.mo-lb-header-spacer { flex: 1; }

.mo-lb-media {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 0 16px;
}

.mo-lightbox-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  border-radius: var(--radius-sm);
}

.mo-lightbox-video {
  background: #000;
}

.mo-lb-footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 12px 8px 32px;
  flex-shrink: 0;
}

.mo-lb-btn {
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-full);
  padding: 0;
  color: inherit;
  flex-shrink: 0;
  transition: background 120ms ease;
}
.mo-lb-btn:hover { background: rgba(255, 255, 255, 0.08); }

/* ── mobile adjustments ───────────────────────────────────── */

@media (max-width: 640px) {
  .mo-panel {
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    max-height: 96vh;
  }

  .mo-medium,
  .mo-large {
    width: 100vw;
    align-self: flex-end;
  }
}

/* New Reminder creation surface */

.nr-root {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bg-body);
  position: relative;
}

/* ── Header ────────────────────────────────────────────────── */

.nr-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-sm) var(--sp-md);
  gap: var(--sp-sm);
  flex-shrink: 0;
}

.nr-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--text-primary);
  text-decoration: none;
  border-radius: var(--radius-full);
  transition: background 120ms ease;
}
.nr-back:hover { background: var(--bg-secondary); text-decoration: none; }

.nr-header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
}

/* Pill buttons: circular icon buttons in the header */
.nr-pill-btn {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 120ms ease, border-color 120ms ease;
  padding: 0;
  font: inherit;
  border: none;
}
.nr-pill-btn:hover:not(:disabled) {
  background: var(--bg-secondary);
}
.nr-pill-btn--disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

/* Done button — inactive: same gray as others */
/* Done — disabled/no-changes state: tertiary bg, tertiary icon.
   The green fill lives on a ::before overlay whose OPACITY fades —
   CSS cannot transition between a color and a gradient image, which
   is why a `transition: background` here snapped instantly. */
.nr-pill-btn--done {
  position: relative;
  background: var(--bg-tertiary);
  border-color: transparent;
  color: var(--text-tertiary);
  cursor: not-allowed;
  pointer-events: none;
  transition: color 0.3s ease-in-out, transform 0.3s ease-in-out;
}

.nr-pill-btn--done::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--fill-gradient);
  opacity: 0;
  transition: opacity 0.3s ease-in-out;
}

/* Keep the glyph above the gradient overlay. */
.nr-pill-btn--done > .icon {
  position: relative;
  z-index: 1;
}

/* Done button — active: fade the gradient in, icon to white, gentle pop */
.nr-pill-btn--done.nr-pill-btn--active {
  border-color: transparent;
  color: #ffffff;
  transform: scale(1.04);
  /* The base .nr-pill-btn--done rule sets pointer-events:none/cursor:not-allowed
     for the disabled look; the active state must restore them or the click
     never reaches the button. */
  pointer-events: auto;
  cursor: pointer;
}
.nr-pill-btn--done.nr-pill-btn--active::before {
  opacity: 1;
}
.nr-pill-btn--done.nr-pill-btn--active:hover {
  filter: brightness(1.05);
}

/* Full-width hero removed from reminder (container) views — media
   renders in the title-row thumbnail deck instead (iOS parity). */

/* ── Body ──────────────────────────────────────────────────── */

/* No horizontal padding on the container — each child carries its own
   so the hero above can bleed edge-to-edge. */
.nr-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-top: var(--sp-md);
  padding-bottom: 0;
  gap: var(--sp-xs);
  /* Single page-level scroll container: the reminder card and (on detail)
     the AI chat section below it both flow here — neither owns its own
     independent scrollbox. The extra breathing room between the card and
     the AI chat section comes from .detail-ai-chat's own margin-top, same
     as before this was split into a separate box. */
  overflow-y: auto;
}
@media (max-width: 768px) {
  .nr-body { padding: calc(var(--sp-sm) + var(--sp-xs)) 0 0 0;
  }
}

/* Reminder-editing content (title/due/body/checklist) — its own card.
   height: fit-content sizes it exactly to its content (no max-height
   anywhere in this chain either), so it keeps growing as content (title
   lines, notes, checklist items) is added and never clips or scrolls
   internally. Kept as a dedicated wrapper (rather than styling .nr-body
   directly) so the detail page can render the AI chat section as an
   unboxed sibling below it instead of inside the same box. */
.nr-reminder-card {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--sp-xs);
  height: fit-content;
}

/* Heading block: title row + due pill in one container that owns the
   horizontal/vertical padding and the bottom hairline (replaces the old
   standalone .nr-divider below the pill). */
.reminder-heading-container {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-md); /* 16px title block → due pill */
  padding: 0 var(--sp-xl) var(--sp-md) var(--sp-xl);
  border-bottom: 1px solid var(--border-tertiary);
  flex-shrink: 0;
}

/* Title row: completion circle + textarea + optional thumbnail deck
   (.thumb-stack, styles in detail.css). The deck is pulled OUT OF FLOW
   (absolute, top-right of the heading container) so the row's height
   tracks the title text only — otherwise the 80px deck box forces a
   large gap between a short title and the due pill below. The rotated
   fan overhangs harmlessly. */
.nr-title-row {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-sm);
}

.reminder-heading-container .thumb-stack {
  position: absolute;
  top: 0;
  right: var(--sp-xl);
  margin-left: 0;
}

/* Reserve the deck's width so the title wraps before running under it. */
.reminder-heading-container:has(.thumb-stack) .nr-title {
  margin-right: 96px; /* 80px deck + 16px breathing room */
}

/* Due/reminder pill row — directly below the title block (iOS
   ReminderPill). Pill visuals come from the shared .due-pill component
   (pill.css); spacing/inset come from the heading container. */
.nr-due-row {
  display: flex;
}

/* (Hairline below the pill now lives on .reminder-heading-container's
   bottom border.) */

.nr-completion-circle {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  border: 2px solid var(--text-tertiary);
  border-radius: var(--radius-full);
  margin-top: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  cursor: pointer;
  padding: 0;
  font: inherit;
  transition: border-color 150ms ease, background 150ms ease;
}
.nr-completion-circle:hover {
  border-color: var(--text-secondary);
}
/* Completed state: bare checkmark/thick glyph, label-tertiary — the
   button drops its ring and hugs the 24px svg (mock parity). */
.nr-completion-circle--done {
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  padding: 0;
}
.nr-completion-circle--done:hover {
  border-color: transparent;
  color: var(--text-secondary);
}

/* Reminder list/picker section */
.nr-body .detail-reminders-list,
.nr-body .detail-action-remind-wrapper {
  padding: 0 var(--sp-xl);
}

.nr-title {
  flex: 1;
  min-width: 0;
  font-size: var(--text-3xl);
  font-weight: 600;
  color: var(--text-primary);
  background: transparent;
  border: none;
  outline: none;
  resize: none;
  font-family: inherit;
  line-height: 1.3;
  padding: 0;
  /* Auto-grow with content, capped at ~3 lines (matches
     .detail-title-editable's treatment and the iOS large title). */
  field-sizing: content;
  min-height: 1.3em;
  max-height: calc(3 * 1.3em);
  overflow: hidden;
}

.nr-title::placeholder { color: var(--text-tertiary); }

/* Completed state — iOS EditableTitle parity: strikethrough +
   textPrimary → textSecondary. Title-only; due pill, body, and thumbnail
   deck are unaffected. */
.nr-title--done {
  text-decoration: line-through;
  color: var(--text-secondary);
}

/* Milkdown body editor mount. Typography and ProseMirror styling come
   from the shared .detail-body-editor rules (detail.css) — the mount div
   carries both classes.
   Flex sizing: `flex: 1 0 auto` (grow, NEVER shrink, content basis). The
   card is meant to look spacious by default (`.nr-reminder-card`'s
   min-height: 50vh) rather than hug tightly, so any leftover slack should
   read as more room to type — not dead unclickable padding below the
   editor — which is why the editor (not some inert spacer) is the child
   that claims it. Originally this was `flex: 1` (basis 0, shrink 1),
   which let the editor get squeezed toward its min-height once the AI
   chat filled the column; grow:1 + shrink:0 avoids that. */
.nr-body-editor {
  flex: 1 0 auto;
  min-height: 120px;
  /* Full-bleed editable surface: the inset moves INTO the ProseMirror
     node below so the entire remaining area is clickable/editable —
     with padding out here, clicks in the gutter landed on the mount
     div instead of the editor. */
  padding: 0;
  display: flex;
  flex-direction: column;
  cursor: text;
  outline: none;
}

/* Stretch the Milkdown chain to fill the mount (detail.css sets
   display:block — the flex column here wins for both surfaces since the
   mount carries .nr-body-editor on NR and detail alike). */
.nr-body-editor .milkdown {
  display: flex;
  flex-direction: column;
  flex: 1;
  width: 100%;
}

.nr-body-editor .ProseMirror {
  flex: 1;
  width: 100%;
  box-sizing: border-box;
  /* The visual inset the mount used to carry. */
  padding: var(--sp-lg) var(--sp-xl);
}

/* Override the detail editor's placeholder copy for this page. */
.nr-body-editor .ProseMirror p.is-editor-empty:first-child::before,
.nr-body-editor .ProseMirror:empty::before {
  content: "Reminder";
}

/* ── Toolbar ───────────────────────────────────────────────── */

/* iOS CaptureBottomBar cluster layout:
     [back circle] [media/formatting capsule] [⋯ circle] [confirm circle]
   Grey circles + capsule on the surface fill; the confirm circle carries
   the green Done gradient. The detail surface reuses .nr-toolbar /
   .nr-toolbar-btn / .nr-toolbar-spacer with a flat button row (no capsule
   or circles), so those base rules must stay detail-compatible.

   Toolbar background continues its upper-most sibling's surface:
   New Reminder (body) → bg-primary; detail (AI-chat section above it)
   → bg-secondary via the .nr-root--detail modifier. */
.nr-toolbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md) var(--sp-xl) var(--sp-md);
  padding-bottom: var(--sp-xl);
  flex-shrink: 0;
}

.nr-root--detail .nr-toolbar {
  background: var(--bg-secondary);
}

.nr-toolbar-spacer { flex: 1; }

/* Flat icon button — used bare on the detail toolbar and inside the
   New Reminder capsule (transparent fill either way). */
.nr-toolbar-btn {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-full);
  color: var(--text-primary);
  padding: 0;
  font: inherit;
  flex-shrink: 0;
  transition: background 120ms ease;
}
.nr-toolbar-btn:hover:not(:disabled) { background: var(--bg-secondary); }
/* Formatting commands the editor FFI can't run yet render disabled. */
.nr-toolbar-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Capsule mode-toggle chevron — narrower hit area, like iOS's 36pt
   capsuleChevronButton next to 44pt icon buttons. */
.nr-toolbar-btn--chevron {
  width: 32px;
}

/* Center capsule — the media-attach strip that flips to formatting. */
.nr-toolbar-capsule {
  min-width: fit-content;
  max-width: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 var(--sp-xs);
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  overflow: hidden;
}

/* Circular grey buttons — leading back and trailing ⋯ (iOS 48pt → 44px). */
.nr-toolbar-circle {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--radius-full);
  background: var(--bg-secondary);
  color: var(--text-primary);
  cursor: pointer;
  padding: 0;
  font: inherit;
  flex-shrink: 0;
  transition: filter 120ms ease, background 120ms ease;
}
.nr-toolbar-circle:hover:not(:disabled) { filter: brightness(0.96); }

/* The trailing circle renders as an <a> while it's the New Reminder CTA
   (keyboard down) — keep link chrome off the icon. */
a.nr-toolbar-circle,
a.nr-toolbar-circle:hover {
  text-decoration: none;
}

/* Trailing confirm circle — grey disabled + while there's nothing to
   save (iOS editing-empty quaternary fill), green gradient ✓ once
   content exists (same gradient family as the detail header's Done). */
.nr-toolbar-confirm:disabled {
  color: var(--text-tertiary);
  cursor: not-allowed;
}
.nr-toolbar-confirm--active {
  background: var(--fill-gradient);
  color: #ffffff;
}
/* Chained with .nr-toolbar-circle so this beats the grey circles'
   darkening hover rule (same element carries both classes). */
.nr-toolbar-circle.nr-toolbar-confirm--active:hover {
  filter: brightness(1.05);
}

/* ── Accessory bar — responsive split ───────────────────────────────── */
/* ui/accessory_bar renders BOTH the mobile cluster bar (.nr-toolbar) and
   the desktop floating rail (.acc-rail); width media queries gate which
   one shows (same visibility-pin pattern as .page-header).

   Both reminder surfaces (New Reminder + owner detail) render the
   component, so the desktop restructure below applies to every .nr-root. */

/* Mobile bar buttons — mock parity: white chrome with a hairline border
   (was solid bg-secondary fill). */
.nr-toolbar-circle,
.nr-toolbar-capsule {
  background: var(--bg-primary);
  border: 1px solid var(--border-tertiary);
}
/* The active confirm keeps its gradient fill (class order must not
   matter, so re-assert over the border rule above). */
.nr-toolbar-circle.nr-toolbar-confirm--active {
  background: var(--fill-gradient);
  border-color: transparent;
}

.acc-rail {
  display: none;
}

@media (min-width: 769px) {
  .nr-root .nr-toolbar {
    display: none;
  }

  /* Mobile-only feature (see ai_chat.view_composer_bar) — desktop keeps
     the static .acc-rail, so the whole dock (row + staged strip) hides. */
  .ai-composer-dock {
    display: none;
  }

  /* Floating vertical rail in the card's right gutter. Horizontal
     position hugs the card edge: 50% + half the card width + gap, where
     card width = min(960px, 100% - 224px) (see .nr-body below). */
  .acc-rail {
    position: absolute;
    left: calc(50% + min(480px, 50% - 112px) + var(--sp-md));
    top: 120px; /* below the floating header circles — tune with design */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-lg);
    z-index: 5;
  }

  .acc-rail-pill {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-xs);
    padding: var(--sp-sm) var(--sp-xs);
    background: var(--bg-primary);
    border-radius: var(--radius-full);
    box-shadow: var(--shadow);
  }

  .acc-rail-btn {
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    cursor: pointer;
    border-radius: var(--radius-full);
    color: var(--text-primary);
    padding: 0;
    font: inherit;
    transition: background 120ms ease;
  }
  .acc-rail-btn:hover:not(:disabled) {
    background: var(--bg-secondary);
  }
  /* Formatting commands the editor FFI can't run yet render disabled. */
  .acc-rail-btn:disabled {
    opacity: 0.35;
    cursor: default;
  }

  /* Detached green circle below the pill — the global "New Reminder"
     CTA (always green; saving happens via the header ✓, not here). */
  .acc-rail-cta {
    width: 52px;
    height: 52px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full);
    background: var(--fill-gradient);
    color: #ffffff;
    box-shadow: var(--shadow);
    text-decoration: none;
    transition: filter 120ms ease;
  }
  .acc-rail-cta:hover {
    filter: brightness(1.05);
    text-decoration: none;
  }
}

/* Hidden multi-file input behind the accessory bar's folder action. */
.nr-attach-input {
  display: none;
}

/* Primary action in the capture surfaces (Save recording / Use photo) —
   token-based replacement for the old dark-theme .capture-sheet-save-btn. */
.nr-capture-save-btn {
  padding: var(--sp-sm) var(--sp-xl);
  background: var(--fill-gradient);
  color: #ffffff;
  font: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  border: none;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: filter 120ms ease;
}
.nr-capture-save-btn:hover {
  filter: brightness(1.05);
}

/* (Camera modal visuals come from detail.css's .camera-modal-body /
   .camera-viewfinder and capture.css's .capture-webcam-preview rules.) */

/* (Local media previews render through the shared .media-viewer chrome —
   styles in detail.css — and the shared image lightbox in modal.css.) */

/* ── Voice-memo capture overlay (iOS VoicePickerView parity) ────────── */
/* Fullscreen, PERMANENTLY DARK layer floating over the dimmed/blurred
   page — colors are pinned literals (iOS forces .dark on this modal), so
   the app theme must not leak in. Sits above the sheet layer (z 400/401). */
.voice-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  flex-direction: column;
  background: rgba(20, 20, 22, 0.72);
  -webkit-backdrop-filter: blur(24px);
  backdrop-filter: blur(24px);
}

/* Waveform zone — dotted midline (AudioWaveformLine idle) + timer. */
.voice-overlay__wave {
  flex: 3 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
  padding: 0 var(--sp-md);
}
.voice-overlay__dotline {
  width: 100%;
  border-top: 2px dotted rgba(235, 235, 245, 0.35);
}
.voice-overlay__timer {
  font-variant-numeric: tabular-nums;
  font-size: var(--text-base);
  color: rgba(235, 235, 245, 0.6);
}

.voice-overlay__error {
  margin: 0;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--accent-red);
}

/* Record toggle (VoiceRecordButton): label over a 64px circle. */
.voice-overlay__record {
  flex: 2 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-md);
}
.voice-overlay__record-label {
  font-size: var(--text-base);
  color: rgba(235, 235, 245, 0.6);
}
/* Idle / rerecord: ghost outlined mic, white glyph. */
.voice-record-btn {
  width: 64px;
  height: 64px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  background: transparent;
  border: 1px solid rgba(235, 235, 245, 0.3);
  color: #ffffff;
  cursor: pointer;
  padding: 0;
  font: inherit;
}
/* Recording: red fill + detached outer ring (outline-offset = the 4px
   gap iOS draws with the padded overlay circle). */
.voice-record-btn--recording {
  background: var(--accent-red);
  border-color: transparent;
  outline: 1.5px solid var(--accent-red);
  outline-offset: 4px;
}

/* Bottom bar — Cancel / Done wide capsules (56px, fill the row). */
.voice-overlay__actions {
  display: flex;
  gap: var(--sp-md);
  padding: 0 var(--sp-md) calc(var(--sp-md) + env(safe-area-inset-bottom));
}
.voice-capsule {
  flex: 1;
  height: 56px;
  border: none;
  border-radius: var(--radius-full);
  font: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  cursor: pointer;
  background: rgba(118, 118, 128, 0.24);
  color: #ffffff;
}
/* Done — dark-pinned Inverse palette: dim while disabled, white/black
   once a take exists. */
.voice-capsule--done-disabled {
  background: rgba(118, 118, 128, 0.18);
  color: rgba(235, 235, 245, 0.3);
  cursor: not-allowed;
}
.voice-capsule--done {
  background: #ffffff;
  color: #000000;
}
.voice-capsule--done:hover {
  filter: brightness(0.95);
}

/* ── Desktop card shell (New Reminder) ──────────────────────────────── */
/* Mock: grey canvas, centered white content card, floating back circle
   top-left + save ✓ top-right, rail in the right gutter. */
@media (min-width: 769px) {
  .nr-root {
    background: var(--bg-secondary);
  }

  .nr-root .nr-header {
    padding: var(--sp-lg) var(--sp-xl);
  }

  .nr-root .nr-back {
    width: 44px;
    height: 44px;
  }
  .nr-root .nr-back:hover {
    background: var(--bg-primary);
    filter: brightness(0.96);
  }

  .nr-root .nr-pill-btn--done {
    width: 44px;
    height: 44px;
  }

  /* .nr-body itself stays a plain (transparent, unboxed) scroll column,
     centered with gutters wide enough for the rail (112px each side at
     the narrow end) — both the reminder card and, on detail, the AI chat
     section below it line up under this same width. */
  .nr-root .nr-body {
    width: min(960px, calc(100% - 224px));
    margin: 0 auto var(--sp-xl);
  }

  /* The reminder card becomes the white, rounded box (mock parity) —
     moved off .nr-body so the AI chat section can render below it,
     unboxed, without inheriting the card background. */
  .nr-root .nr-reminder-card {
    background: var(--bg-primary);
    border-radius: var(--radius-xl);
    padding: var(--sp-xl) 0 var(--sp-lg);
  }
}

/* Mobile: no top chrome at all — the accessory bar owns back + confirm
   (mock parity). Detail keeps its header until it adopts the rail. */
@media (max-width: 768px) {
  .nr-root .nr-header {
    display: none;
  }
  .nr-toolbar-capsule {
    height: var(--size-5xl);
  }
  .nr-toolbar-btn, .nr-toolbar-circle {
    height: var(--size-5xl);
    width: var(--size-5xl);
  }
}


@media (max-width: 488px) {
    .nr-toolbar-circle {
        height: var(--size-4xl);
        width: var(--size-4xl);
    }
    .nr-toolbar-capsule {
        height: var(--size-4xl);
    }

    .ai-composer-pill {
        min-height: var(--size-4xl);
    }

    .nr-toolbar-btn {
      height: var(--size-xxl);
      width: var(--size-xxl);
    }
}

/* ── Docked Please AI composer bar (mobile detail toolbar default) ───
   Replaces the .nr-toolbar-capsule + trailing circle with a docked chat
   composer when neither the title nor the body editor has focus (iOS
   EditorFocus / CaptureChatComposerBar parity — see ai_chat.gleam's
   `view_composer_bar`). Desktop is unaffected (hidden above via
   .ai-composer-dock).

   The row itself reuses .nr-toolbar for its outer chrome (padding,
   background, the bg-secondary swap on .nr-root--detail) and
   .nr-toolbar-circle / .nr-toolbar-confirm for the back/⋯/+ circles — no
   new rules needed for those. Everything below is new, dedicated markup
   so chat.css's chat-composer-* rules (the Messages composer) are never
   touched. The staged-attachment strip is the one exception: it reuses
   chat.css's pending-image chip classes for the chip itself (read-only —
   this file adds no chat.css overrides), only the scroll row wrapper
   below is new. */

.ai-composer-dock {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  position: relative;
  z-index: 1;
}

.ai-composer-dock-staged {
  display: flex;
  flex-direction: row;
  gap: var(--sp-sm);
  overflow-x: auto;
  padding: var(--sp-sm) var(--sp-md) 0 var(--sp-md);
  scrollbar-width: none;
}
.ai-composer-dock-staged::-webkit-scrollbar {
  display: none;
}

/* Fills the space the capsule normally takes — no max-width, unlike
   .nr-toolbar-capsule, since iOS's InputPill spans the row. */
.ai-composer-pill {
  flex: 1;
  min-width: 0;
  display: flex;
  /* Centered by default (empty/short single-line draft) — see
     .ai-composer-dock--grown below for the bottom-aligned, multi-line
     state. */
  align-items: center;
  gap: var(--sp-xs);
  /* Floor, not a cap — .ai-composer-pill-input grows the pill taller as
     the draft grows (like Messages/iMessage), rather than clipping it to
     a single line and scrolling the text inside a cramped box. */
  min-height: var(--size-5xl);
  padding: 0 var(--sp-xs) 0 8px;
  /* Fixed (not --radius-full): stays a full pill at the collapsed
     min-height, but doesn't stretch into an ellipse as the box grows —
     reads as a squircle instead. */
  border-radius: 32px;
  background: var(--bg-primary);
  border: 1px solid var(--border-tertiary);
}

/* Toggled by dom.mjs (autoResizeTextarea/resetTextarea) on the outer
   .ai-composer-dock once the textarea's own scrollHeight grows past a
   single line — i.e. the pill has actually gotten taller than its
   baseline, not just gained a couple of characters. Bottom-aligns the
   pill's attach/send icons and the dock row's ⋯ button to the composer's
   now-multi-line baseline (text still reads top-down, inherent to the
   textarea) instead of floating centered against the grown height.
   Stays applied until the draft is cleared entirely — see dom.mjs. */
.ai-composer-dock--grown .ai-composer-dock-row,
.ai-composer-dock--grown .ai-composer-pill {
  align-items: flex-end;
}

.ai-composer-dock--grown .ai-composer-pill {
  padding-bottom: 8px;
}

/* Tighter trailing padding once the send button is showing (iOS's
   InputPill: 6px vs the placeholder state's roomier trailing padding). */
.ai-composer-pill--can-send {
  padding-right: 8px;
}

.ai-composer-pill-file-input {
  display: none;
}

.ai-composer-pill-attach {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  background: var(--fill-elevated-primary);
  color: var(--text-secondary);
  cursor: pointer;
}

.ai-composer-pill-input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  outline: none;
  resize: none;
  /* Grows with the draft (dom_ffi.auto_resize_textarea sets an explicit
     px height on input, matching scrollHeight) up to this cap, then
     scrolls internally — same generous grow-then-scroll convention as
     chat.css's .chat-edit-milkdown, instead of the old 96px cap that
     made multi-line drafts scroll in a cramped box almost immediately. */
  max-height: 40vh;
  overflow-y: auto;
  line-height: 20px;
  padding: 8px 0;
  margin: 0;
}
.ai-composer-pill-input::placeholder {
  color: var(--text-tertiary);
}
.ai-composer-pill-input:disabled {
  opacity: 0.6;
}

.ai-composer-pill-send {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: var(--bg-primary-inverse);
  color: var(--text-primary-inverse);
  transition: background 0.15s ease;
}
.ai-composer-pill-send:disabled {
  background: var(--bg-quarternary);
  color: var(--text-disabled);
  cursor: default;
}

/* ── Owner-container ⋮ action sheet ─────────────────────────────────── */

.nr-action-sheet {
  padding-bottom: env(safe-area-inset-bottom);
}

.nr-action-sheet__close {
  position: absolute;
  top: var(--sp-sm);
  right: var(--sp-md);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0;
}

.nr-action-row {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  width: 100%;
  padding: var(--sp-lg);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: var(--text-primary);
}
/* First row sits under the close button / handle; no top divider. */
.nr-action-row:first-of-type {
  border-top: none;
}
.nr-action-row:hover {
  background: var(--bg-secondary);
}

.nr-action-row__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-primary);
  flex-shrink: 0;
}

.nr-action-row__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.nr-action-row__label {
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--text-primary);
}

.nr-action-row__sublabel {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.nr-action-row__trailing {
  display: inline-flex;
  align-items: center;
  color: var(--text-tertiary);
  flex-shrink: 0;
  margin-left: var(--sp-sm);
}

/* Sync indicator — fixed pill, bottom-right. Desktop (Tauri) only; the
   Gleam side renders nothing on web, so these rules never match there.

   The resting ("synced") state is deliberately understated: a local-first
   app is synced almost all the time, and a permanent green SYNCED badge is
   just noise. So it collapses to a bare icon at low opacity, and only
   expands into a labelled pill when it has something to say — queued work,
   offline, or a failure. Hover expands it regardless, so the label is
   always reachable. */

.sync-indicator {
    position: fixed;
    right: var(--sp-md);
    bottom: var(--sp-md);
    z-index: 40;

    display: inline-flex;
    align-items: center;
    gap: var(--sp-3xs);
    max-width: 260px;
    padding: var(--sp-3xs) var(--sp-xs);

    border: none;
    border-radius: var(--radius-full);
    background: var(--bg-elevated-secondary);
    color: var(--text-secondary);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);

    font: inherit;
    font-size: var(--text-sm);
    line-height: 16px;
    cursor: pointer;

    opacity: 0.55;
    transition: opacity 0.15s ease, background-color 0.15s ease,
        color 0.15s ease;
}

.sync-indicator:hover,
.sync-indicator:focus-visible {
    opacity: 1;
}

.sync-indicator-icon {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
}

/* Collapsed by default; the states that need to speak open it below.
   Width (not display) so it can animate, and so the label stays in the
   accessibility tree for screen readers even while visually collapsed. */
.sync-indicator-label {
    overflow: hidden;
    max-width: 0;
    white-space: nowrap;
    transition: max-width 0.2s ease;
}

.sync-indicator:hover .sync-indicator-label,
.sync-indicator:focus-visible .sync-indicator-label,
.sync-indicator--syncing .sync-indicator-label,
.sync-indicator--pending .sync-indicator-label,
.sync-indicator--offline .sync-indicator-label,
.sync-indicator--error .sync-indicator-label {
    max-width: 200px;
}

/* ── States ──────────────────────────────────────────────────────────── */

/* Up to date: the quiet one. Fades almost out of the way. */
.sync-indicator--synced {
    opacity: 0.35;
}

/* No sync has completed this launch. Not a failure, but not "up to date"
   either — we have no basis for that claim yet, so it stays neutral and
   quiet rather than showing a reassuring check. */
.sync-indicator--idle {
    opacity: 0.35;
}

.sync-indicator--syncing {
    opacity: 1;
    color: var(--text-secondary);
}

.sync-indicator--syncing .sync-indicator-icon {
    animation: sync-indicator-spin 1s linear infinite;
}

/* Queued work — informational, not alarming. */
.sync-indicator--pending,
.sync-indicator--offline {
    opacity: 1;
    color: var(--text-primary);
    background: var(--fill-elevated-secondary);
}

/* The only genuinely bad state. Offline deliberately is NOT red: being
   offline is a normal, fully-supported mode here, not a failure. */
.sync-indicator--error {
    opacity: 1;
    color: var(--accent-red);
    background: var(--fill-elevated-secondary);
}

@keyframes sync-indicator-spin {
    from {
        transform: rotate(0deg);
    }
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .sync-indicator--syncing .sync-indicator-icon {
        animation: none;
    }

    .sync-indicator,
    .sync-indicator-label {
        transition: none;
    }
}

/* Desktop-only: native window chrome, and turning off the browser-isms.
   Every rule is scoped to `.is-desktop`, so this is inert on web. Last, so it
   can override the shared component styles it needs to. */
/* Desktop-only chrome and de-web-ification.

   Everything here is scoped to `.is-desktop`, a class the Tauri init script
   puts on <html> at document-start. The web build never matches any of it, so
   this file is inert in the browser.

   Two jobs:
     1. Make the window look like an app — the traffic lights float over our
        own content instead of sitting in a stock title bar strip above it.
     2. Take away the tells. Overscroll bounce, selectable chrome, and a page
        that navigates when you drop a file on it are all things a native app
        does not do, and each one individually says "this is a web page". */

/* ── Native window chrome ──────────────────────────────────────────────── */

/* The titlebar is hidden (TitleBarStyle::Overlay), so the webview owns the
   whole window and the traffic lights float on top of it. Two consequences to
   handle: the window needs somewhere to be dragged from, and nothing may
   render underneath the buttons.

   The drag strip itself is NOT css. `-webkit-app-region: drag` is a
   Chromium/Electron feature that WKWebView ignores — Tauri drags start from
   an explicit `startDragging()` call. `installDesktopChrome` in
   ffi/platform.mjs owns the strip: a mousedown in the top 38px that isn't on
   an interactive element starts a native drag, and a double-click there
   zooms. If the strip height changes, change DRAG_STRIP_HEIGHT there and the
   padding below together. */

/* Reserve room for the traffic lights so the sidebar's top item doesn't sit
   under them. The sidebar runs to the top edge — that's the point — it just
   starts its *content* below the buttons. */
.is-desktop .sidebar {
  padding-top: 30px;
}

/* ── Take away the web-isms ────────────────────────────────────────────── */

/* Rubber-band scroll past the end. Nothing native does this, and it's
   probably the single loudest tell. `none` on *everything*: it's strictly
   stronger than the `contain` the scrollers (feed, chat thread) declare for
   the web — same chaining prevention, plus no bounce at the scroller's own
   ends — so any future scroller is covered without a desktop variant. */
.is-desktop,
.is-desktop body,
.is-desktop * {
  overscroll-behavior: none;
}

/* Chrome isn't text. Selecting a sidebar label by dragging across it is
   something that happens on web pages, not in apps. Content — notes, chat
   messages, anything the user might legitimately want to copy — opts back in
   below. */
.is-desktop {
  -webkit-user-select: none;
  user-select: none;
  /* No tap-highlight flash on click. */
  -webkit-tap-highlight-color: transparent;
}

/* ...but real content is still selectable. This list is deliberately generous:
   the failure mode of over-restricting (a user can't copy their own note) is
   far worse than the failure mode of under-restricting (a stray label can be
   highlighted). */
.is-desktop input,
.is-desktop textarea,
.is-desktop [contenteditable],
.is-desktop p,
.is-desktop code,
.is-desktop pre,
.is-desktop .tl-row-title,
.is-desktop .detail-body,
.is-desktop .chat-message-bubble,
.is-desktop .milkdown {
  -webkit-user-select: text;
  user-select: text;
}

/* Dragging an image out of the app, or the ghost-image that trails the cursor
   when you accidentally drag one — both are browser behaviours. */
.is-desktop img {
  -webkit-user-drag: none;
  user-drag: none;
}

