umanggarg Claude Sonnet 4.6 commited on
Commit
db68f24
Β·
1 Parent(s): 6d64237

design: Raycast-style suggestion cards + staggered entrance animations

Browse files

Redesign suggestion buttons from plain text rows to two-line icon cards:
- Left: 36px rounded icon container (category-specific SVG)
- Center: bold title + dim subtitle
- Right: arrow that slides on hover

Add staggered entrance animations (suggestionIn / onboardIn keyframes)
to suggestion cards, onboarding steps, and the suggest-state header.
Content now reveals sequentially rather than appearing all at once.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. ui/src/App.jsx +76 -35
  2. ui/src/index.css +96 -42
ui/src/App.jsx CHANGED
@@ -5,6 +5,24 @@ import Message from "./components/Message";
5
  import DiagramView from "./components/DiagramView";
6
  import { fetchRepos, streamQuery, streamAgentQuery, fetchMcpStatus, fetchMcpPrompt, fetchAgentModels } from "./api";
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  export default function App() {
9
  const [repos, setRepos] = useState([]);
10
  const [reposLoading, setReposLoading] = useState(true);
@@ -717,17 +735,25 @@ export default function App() {
717
  <p>Searching <strong>{repos.length} indexed repos</strong> at once β€” {repos.map(r => r.slug.split("/")[1]).join(", ")}. Results show which repo each source comes from.</p>
718
  <div className="suggestions">
719
  {[
720
- "Compare the architectures of these repos β€” what patterns do they share?",
721
- "Which repo is most complex and what makes it that way?",
722
- "Find all the main entry points across these repos",
723
- "How do these repos handle configuration and environment setup?",
724
- "What are the common abstractions or design patterns across repos?",
725
- ].map(q => (
726
- <button key={q} className="suggestion-btn"
727
- onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
728
- {q}
729
- </button>
730
- ))}
 
 
 
 
 
 
 
 
731
  </div>
732
  </div>
733
  ) : !activeRepo || activeRepo === "all" ? (
@@ -781,21 +807,29 @@ export default function App() {
781
  {agentMode ? (
782
  <>
783
  <div className="mode-hint" style={{ marginBottom: 12 }}>
784
- <strong>Agent mode</strong> runs the ReAct loop β€” search β†’ observe β†’ reason β†’ search again. This is the same pattern used in production agents. Watch the tool calls trace as it works.
785
  </div>
786
  <div className="suggestions">
787
  {[
788
- `Walk through ${activeRepo.split("/")[1]}'s architecture from entry point to output`,
789
- "What are the most important functions and how do they connect?",
790
- "Draw a diagram showing how the main components connect",
791
- "How is error handling and edge cases managed across the codebase?",
792
- "How does data flow from input to the final result?",
793
- ].map(q => (
794
- <button key={q} className="suggestion-btn"
795
- onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
796
- {q}
797
- </button>
798
- ))}
 
 
 
 
 
 
 
 
799
  </div>
800
  <button className="graph-hint-btn" onClick={() => setView("graph")}>
801
  Explore Diagrams for {activeRepo.split("/")[1]} β†’
@@ -803,20 +837,27 @@ export default function App() {
803
  </>
804
  ) : (
805
  <>
806
- <p>Try one of these or ask your own:</p>
807
  <div className="suggestions">
808
  {[
809
- `What is the overall architecture of ${activeRepo.split("/")[1]}?`,
810
- "What are the main entry points and how does the code flow?",
811
- "What are the key classes and what does each one do?",
812
- "How is data processed and transformed through the system?",
813
- "What are the external dependencies and how are they used?",
814
- ].map(q => (
815
- <button key={q} className="suggestion-btn"
816
- onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
817
- {q}
818
- </button>
819
- ))}
 
 
 
 
 
 
 
 
820
  </div>
821
  {/* Secondary action row β€” below suggestions so it doesn't compete */}
822
  <div className="suggest-footer">
 
5
  import DiagramView from "./components/DiagramView";
6
  import { fetchRepos, streamQuery, streamAgentQuery, fetchMcpStatus, fetchMcpPrompt, fetchAgentModels } from "./api";
7
 
8
+ // ── Suggestion card icons ────────────────────────────────────────────────────
9
+ // Simple 16Γ—16 line-art SVGs for each suggestion category.
10
+ // Kept inline so there's no icon-library dependency.
11
+ const ICONS = {
12
+ architecture: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="2.5" r="1.5"/><circle cx="2.5" cy="13" r="1.5"/><circle cx="13.5" cy="13" r="1.5"/><path d="M8 4v3M8 7l-4 4.5M8 7l4 4.5"/></svg>,
13
+ entry: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M6 3H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h3"/><path d="M10 11l4-3-4-3"/><path d="M5 8h9"/></svg>,
14
+ classes: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="4" width="6" height="8" rx="1"/><rect x="9" y="1" width="6" height="4" rx="1"/><rect x="9" y="8" width="6" height="4" rx="1"/><path d="M7 8h2M7 10l2-2"/></svg>,
15
+ flow: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M1 8h3l2-4 3 8 2-4h4"/></svg>,
16
+ functions: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M4 2a2 2 0 0 0-2 2v2a2 2 0 0 1-2 2 2 2 0 0 1 2 2v2a2 2 0 0 0 2 2"/><path d="M12 2a2 2 0 0 1 2 2v2a2 2 0 0 0 2 2 2 2 0 0 0-2 2v2a2 2 0 0 1-2 2"/></svg>,
17
+ diagram: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="1" width="5" height="4" rx="1"/><rect x="10" y="1" width="5" height="4" rx="1"/><rect x="5" y="11" width="6" height="4" rx="1"/><path d="M3.5 5v2a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V5M8 8v3"/></svg>,
18
+ shield: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M8 1L2 3.5v4.3C2 11.1 4.7 13.9 8 15c3.3-1.1 6-3.9 6-7.2V3.5L8 1z"/><path d="M5.5 8l1.5 1.5 3-3"/></svg>,
19
+ package: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M8 1l7 4v6l-7 4L1 11V5l7-4z"/><path d="M1 5l7 4M15 5l-7 4M8 9v6"/></svg>,
20
+ compare: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 3H2v10h3M11 3h3v10h-3"/><path d="M5 8h6M8 5l3 3-3 3"/></svg>,
21
+ complexity: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M2 14L6 6l3 4 2-5 3 8"/></svg>,
22
+ config: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="8" cy="8" r="2"/><path d="M8 2v1M8 13v1M2 8H1M15 8h-1M3.9 3.9l.7.7M11.4 11.4l.7.7M3.9 12.1l.7-.7M11.4 4.6l.7-.7"/></svg>,
23
+ pattern: <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="1" y="1" width="4" height="4" rx="1"/><rect x="6" y="1" width="4" height="4" rx="1"/><rect x="11" y="1" width="4" height="4" rx="1"/><rect x="1" y="6" width="4" height="4" rx="1"/><rect x="6" y="11" width="4" height="4" rx="1"/><rect x="11" y="6" width="4" height="4" rx="1"/></svg>,
24
+ };
25
+
26
  export default function App() {
27
  const [repos, setRepos] = useState([]);
28
  const [reposLoading, setReposLoading] = useState(true);
 
735
  <p>Searching <strong>{repos.length} indexed repos</strong> at once β€” {repos.map(r => r.slug.split("/")[1]).join(", ")}. Results show which repo each source comes from.</p>
736
  <div className="suggestions">
737
  {[
738
+ { icon: "compare", title: "Compare architectures", body: "What patterns do these repos share?" },
739
+ { icon: "complexity", title: "Complexity analysis", body: "Which repo is most complex and why?" },
740
+ { icon: "entry", title: "Entry points", body: "Find all main entry points across repos" },
741
+ { icon: "config", title: "Configuration", body: "How do these repos handle env & config?" },
742
+ { icon: "pattern", title: "Design patterns", body: "Common abstractions across all repos" },
743
+ ].map(({ icon, title, body }) => {
744
+ const q = `${title}: ${body}`;
745
+ return (
746
+ <button key={title} className="suggestion-btn"
747
+ onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
748
+ <span className="suggestion-icon">{ICONS[icon]}</span>
749
+ <span className="suggestion-content">
750
+ <span className="suggestion-title">{title}</span>
751
+ <span className="suggestion-body">{body}</span>
752
+ </span>
753
+ <svg className="suggestion-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
754
+ </button>
755
+ );
756
+ })}
757
  </div>
758
  </div>
759
  ) : !activeRepo || activeRepo === "all" ? (
 
807
  {agentMode ? (
808
  <>
809
  <div className="mode-hint" style={{ marginBottom: 12 }}>
810
+ <strong>Agent mode</strong> β€” search β†’ observe β†’ reason β†’ search again. Watch the ReAct loop work in real time.
811
  </div>
812
  <div className="suggestions">
813
  {[
814
+ { icon: "architecture", title: "Map the architecture", body: `Walk ${activeRepo.split("/")[1]} from entry point to output` },
815
+ { icon: "functions", title: "Key functions", body: "Most important functions and how they connect" },
816
+ { icon: "diagram", title: "Generate a diagram", body: "Visual map of the main components" },
817
+ { icon: "shield", title: "Error handling", body: "How edge cases are managed across the codebase" },
818
+ { icon: "flow", title: "Data flow", body: "How data moves from input to final result" },
819
+ ].map(({ icon, title, body }) => {
820
+ const q = `${title}: ${body}`;
821
+ return (
822
+ <button key={title} className="suggestion-btn"
823
+ onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
824
+ <span className="suggestion-icon">{ICONS[icon]}</span>
825
+ <span className="suggestion-content">
826
+ <span className="suggestion-title">{title}</span>
827
+ <span className="suggestion-body">{body}</span>
828
+ </span>
829
+ <svg className="suggestion-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
830
+ </button>
831
+ );
832
+ })}
833
  </div>
834
  <button className="graph-hint-btn" onClick={() => setView("graph")}>
835
  Explore Diagrams for {activeRepo.split("/")[1]} β†’
 
837
  </>
838
  ) : (
839
  <>
 
840
  <div className="suggestions">
841
  {[
842
+ { icon: "architecture", title: "Overall architecture", body: `How is ${activeRepo.split("/")[1]} structured?` },
843
+ { icon: "entry", title: "Entry points", body: "Main entry points and how the code flows" },
844
+ { icon: "classes", title: "Key classes", body: "What each major class does" },
845
+ { icon: "flow", title: "Data processing", body: "How data is transformed through the system" },
846
+ { icon: "package", title: "Dependencies", body: "External libraries and how they're used" },
847
+ ].map(({ icon, title, body }) => {
848
+ const q = `${title}: ${body}`;
849
+ return (
850
+ <button key={title} className="suggestion-btn"
851
+ onClick={() => { setInput(q); textareaRef.current?.focus(); }}>
852
+ <span className="suggestion-icon">{ICONS[icon]}</span>
853
+ <span className="suggestion-content">
854
+ <span className="suggestion-title">{title}</span>
855
+ <span className="suggestion-body">{body}</span>
856
+ </span>
857
+ <svg className="suggestion-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 8h10M9 4l4 4-4 4"/></svg>
858
+ </button>
859
+ );
860
+ })}
861
  </div>
862
  {/* Secondary action row β€” below suggestions so it doesn't compete */}
863
  <div className="suggest-footer">
ui/src/index.css CHANGED
@@ -1330,11 +1330,23 @@ textarea:focus-visible {
1330
  gap: 12px;
1331
  max-width: 580px;
1332
  width: 100%;
1333
- /* Some vertical padding keeps it from sitting right at the flex center line */
1334
  padding-top: 16px;
1335
  padding-bottom: 24px;
1336
  }
1337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1338
  /* Header for onboarding section */
1339
  .onboarding-header {
1340
  display: flex;
@@ -1506,6 +1518,7 @@ textarea:focus-visible {
1506
  /* Suggest state */
1507
  .suggest-state { max-width: 620px; width: 100%; text-align: center; padding-top: 16px; padding-bottom: 24px; }
1508
  .suggest-state h2 {
 
1509
  font-size: 48px;
1510
  font-weight: 300;
1511
  font-family: "Anthropic Serif", Georgia, serif;
@@ -1516,6 +1529,7 @@ textarea:focus-visible {
1516
  text-shadow: 0 0 80px rgba(91,143,249,0.20);
1517
  }
1518
  .suggest-state > p {
 
1519
  font-size: 13.5px;
1520
  color: var(--text-2);
1521
  margin-bottom: 20px;
@@ -1525,66 +1539,106 @@ textarea:focus-visible {
1525
  .suggestions {
1526
  display: flex;
1527
  flex-direction: column;
1528
- gap: 6px;
1529
  margin-bottom: 18px;
1530
  }
1531
 
 
 
 
 
 
 
 
 
 
 
 
1532
  .suggestion-btn {
1533
- background: rgba(255, 255, 255, 0.04);
1534
- border: 1px solid rgba(255, 255, 255, 0.10);
1535
- border-bottom: 1px solid rgba(255, 255, 255, 0.15);
1536
- border-radius: 12px;
 
 
 
1537
  color: var(--text);
1538
  cursor: pointer;
1539
- position: relative;
1540
  font-family: var(--sans);
1541
- font-size: 13.5px;
1542
- font-weight: 450;
1543
- padding: 13px 18px 13px 20px;
1544
  text-align: left;
1545
- transition: border-color var(--transition), background var(--transition), color var(--transition), transform var(--transition), box-shadow var(--transition);
1546
- letter-spacing: -0.01em;
1547
- box-shadow:
1548
- rgba(255, 255, 255, 0.04) 0px 0px 20px 0px inset,
1549
- rgba(91, 143, 249, 0.06) 0px 4px 20px 0px;
1550
  }
1551
 
1552
  .suggestion-btn:hover {
1553
- border-color: rgba(91, 143, 249, 0.65);
1554
- background: rgba(91, 143, 249, 0.12);
1555
- color: var(--text);
1556
- transform: translateX(3px);
1557
  box-shadow:
1558
- rgba(255, 255, 255, 0.25) 0px 0px 24px 0px inset,
1559
- rgba(91, 143, 249, 0.55) 0px 0px 28px 0px,
1560
- rgba(91, 143, 249, 0.75) 0px 0px 0px 1px,
1561
- rgba(91, 143, 249, 0.15) 0px 0px 80px 0px;
 
 
 
 
 
 
 
 
 
1562
  }
1563
 
1564
- .suggestion-btn::before {
1565
- content: '';
1566
- position: absolute;
1567
- left: 0;
1568
- top: 50%;
1569
- transform: translateY(-50%);
1570
- width: 2px;
1571
- height: 55%;
1572
- border-radius: 1px;
1573
- background: var(--accent-border);
1574
- transition: background var(--transition), height var(--transition);
 
 
1575
  }
1576
- .suggestion-btn:hover::before {
1577
- background: var(--accent);
1578
- height: 75%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1579
  }
1580
 
1581
- .suggestion-btn::after {
1582
- content: " β†’";
 
 
 
1583
  color: var(--muted);
1584
- transition: color var(--transition);
1585
- opacity: 0.7;
1586
  }
1587
- .suggestion-btn:hover::after { color: var(--accent-soft); opacity: 1; }
1588
 
1589
  /* ── Mode description β€” one-line explanation below each pill group ── */
1590
  .mode-description {
 
1330
  gap: 12px;
1331
  max-width: 580px;
1332
  width: 100%;
 
1333
  padding-top: 16px;
1334
  padding-bottom: 24px;
1335
  }
1336
 
1337
+ /* Staggered entrance for onboarding header + steps */
1338
+ @keyframes onboardIn {
1339
+ from { opacity: 0; transform: translateY(14px); }
1340
+ to { opacity: 1; transform: translateY(0); }
1341
+ }
1342
+ .onboarding-header {
1343
+ animation: onboardIn 400ms cubic-bezier(0.16, 1, 0.3, 1) both;
1344
+ animation-delay: 0ms;
1345
+ }
1346
+ .onboarding-steps .onboarding-step:nth-child(2) { animation: onboardIn 380ms cubic-bezier(0.16, 1, 0.3, 1) 80ms both; }
1347
+ .onboarding-steps .onboarding-step:nth-child(3) { animation: onboardIn 380ms cubic-bezier(0.16, 1, 0.3, 1) 150ms both; }
1348
+ .onboarding-steps .onboarding-step:nth-child(4) { animation: onboardIn 380ms cubic-bezier(0.16, 1, 0.3, 1) 220ms both; }
1349
+
1350
  /* Header for onboarding section */
1351
  .onboarding-header {
1352
  display: flex;
 
1518
  /* Suggest state */
1519
  .suggest-state { max-width: 620px; width: 100%; text-align: center; padding-top: 16px; padding-bottom: 24px; }
1520
  .suggest-state h2 {
1521
+ animation: onboardIn 400ms cubic-bezier(0.16, 1, 0.3, 1) both;
1522
  font-size: 48px;
1523
  font-weight: 300;
1524
  font-family: "Anthropic Serif", Georgia, serif;
 
1529
  text-shadow: 0 0 80px rgba(91,143,249,0.20);
1530
  }
1531
  .suggest-state > p {
1532
+ animation: onboardIn 380ms cubic-bezier(0.16, 1, 0.3, 1) 60ms both;
1533
  font-size: 13.5px;
1534
  color: var(--text-2);
1535
  margin-bottom: 20px;
 
1539
  .suggestions {
1540
  display: flex;
1541
  flex-direction: column;
1542
+ gap: 7px;
1543
  margin-bottom: 18px;
1544
  }
1545
 
1546
+ /* Staggered entrance β€” each card slides up with a short delay */
1547
+ @keyframes suggestionIn {
1548
+ from { opacity: 0; transform: translateY(10px); }
1549
+ to { opacity: 1; transform: translateY(0); }
1550
+ }
1551
+ .suggestions .suggestion-btn:nth-child(1) { animation-delay: 40ms; }
1552
+ .suggestions .suggestion-btn:nth-child(2) { animation-delay: 90ms; }
1553
+ .suggestions .suggestion-btn:nth-child(3) { animation-delay: 140ms; }
1554
+ .suggestions .suggestion-btn:nth-child(4) { animation-delay: 190ms; }
1555
+ .suggestions .suggestion-btn:nth-child(5) { animation-delay: 240ms; }
1556
+
1557
  .suggestion-btn {
1558
+ display: flex;
1559
+ align-items: center;
1560
+ gap: 14px;
1561
+ background: rgba(255, 255, 255, 0.03);
1562
+ border: 1px solid rgba(255, 255, 255, 0.08);
1563
+ border-bottom-color: rgba(255, 255, 255, 0.12);
1564
+ border-radius: 14px;
1565
  color: var(--text);
1566
  cursor: pointer;
 
1567
  font-family: var(--sans);
1568
+ padding: 13px 14px 13px 14px;
 
 
1569
  text-align: left;
1570
+ transition: border-color var(--transition), background var(--transition), transform var(--transition), box-shadow var(--transition);
1571
+ animation: suggestionIn 320ms cubic-bezier(0.16, 1, 0.3, 1) both;
1572
+ box-shadow: 0 1px 3px rgba(0,0,0,0.25), 0 4px 16px rgba(0,0,0,0.15);
 
 
1573
  }
1574
 
1575
  .suggestion-btn:hover {
1576
+ border-color: rgba(91, 143, 249, 0.50);
1577
+ background: rgba(91, 143, 249, 0.08);
1578
+ transform: translateY(-2px) translateX(1px);
 
1579
  box-shadow:
1580
+ 0 0 0 1px rgba(91, 143, 249, 0.45),
1581
+ 0 0 24px rgba(91, 143, 249, 0.18),
1582
+ 0 8px 24px rgba(0, 0, 0, 0.35);
1583
+ }
1584
+ .suggestion-btn:hover .suggestion-icon {
1585
+ background: rgba(91, 143, 249, 0.18);
1586
+ border-color: rgba(91, 143, 249, 0.40);
1587
+ color: var(--accent-soft);
1588
+ }
1589
+ .suggestion-btn:hover .suggestion-arrow {
1590
+ opacity: 1;
1591
+ transform: translateX(2px);
1592
+ color: var(--accent-soft);
1593
  }
1594
 
1595
+ /* Icon container β€” rounded square with subtle accent tint */
1596
+ .suggestion-icon {
1597
+ width: 36px;
1598
+ height: 36px;
1599
+ min-width: 36px;
1600
+ border-radius: 10px;
1601
+ background: rgba(255, 255, 255, 0.06);
1602
+ border: 1px solid rgba(255, 255, 255, 0.10);
1603
+ display: flex;
1604
+ align-items: center;
1605
+ justify-content: center;
1606
+ color: var(--text-2);
1607
+ transition: background var(--transition), border-color var(--transition), color var(--transition);
1608
  }
1609
+
1610
+ /* Text content β€” title + subtitle */
1611
+ .suggestion-content {
1612
+ display: flex;
1613
+ flex-direction: column;
1614
+ gap: 2px;
1615
+ flex: 1;
1616
+ min-width: 0;
1617
+ }
1618
+ .suggestion-title {
1619
+ font-size: 13.5px;
1620
+ font-weight: 600;
1621
+ color: var(--text);
1622
+ letter-spacing: -0.018em;
1623
+ line-height: 1.3;
1624
+ }
1625
+ .suggestion-body {
1626
+ font-size: 12px;
1627
+ color: var(--muted);
1628
+ letter-spacing: -0.008em;
1629
+ line-height: 1.4;
1630
+ white-space: nowrap;
1631
+ overflow: hidden;
1632
+ text-overflow: ellipsis;
1633
  }
1634
 
1635
+ /* Arrow β€” dim by default, slides right on hover */
1636
+ .suggestion-arrow {
1637
+ opacity: 0.30;
1638
+ flex-shrink: 0;
1639
+ transition: opacity var(--transition), transform var(--transition), color var(--transition);
1640
  color: var(--muted);
 
 
1641
  }
 
1642
 
1643
  /* ── Mode description β€” one-line explanation below each pill group ── */
1644
  .mode-description {