1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* Card
   Flat surface, no border, radius-lg. `--gl-color-surface-card` is one
   neutral step lighter than the page around it — that tonal step, not an
   outline, is what marks the edge. `[data-elevated]` is opt-in and reserved
   for the one focused/primary card in a view — a dialog, the primary
   capture card — flat is the default everywhere else.

   A <div>/<article> is too generic to select bare, so Card keeps one
   sanctioned class. When Card's job is holding a list of rows — the common
   case, entry lists, task lists, settings rows — it is a real <ul class="
   card">, and CardRow is a bare <li> underneath it: no second class needed,
   the row look comes from the `.card > li` descendant selector alone. The
   first row's top border is removed automatically so callers never have to
   override it by hand. */

.card {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--gl-color-surface-card);
  border: none;
  border-radius: var(--gl-radius-lg);
  box-shadow: none;
  overflow: hidden;
}

.card[data-elevated] { box-shadow: var(--gl-shadow-float); }

.card > li {
  display: flex;
  align-items: center;
  gap: var(--gl-space-xs);
  padding: var(--gl-space-xs) var(--gl-space-s);
  border-top: 1px solid var(--gl-color-border-hairline);
  color: var(--gl-color-text-primary);
  font-family: var(--gl-font-sans);
  font-size: var(--gl-step--1);
}
.card > li:first-child { border-top: none; }

/* A row whose ENTIRE content is wrapped in a real <button> or <a> — the
   accessible way to make a row clickable — fills the row edge-to-edge and
   picks up a hover fill and pointer cursor. `:only-child` is what tells this
   apart from a row that merely contains a small button among other content
   (a leading checkbox next to a title and a trailing value, say): only a
   button/anchor that IS the row's one and only child means "the whole row
   is the control." */
.card > li:has(> button:only-child),
.card > li:has(> a:only-child) {
  padding: 0;
}
.card > li > button:only-child,
.card > li > a:only-child {
  all: unset;
  display: flex;
  align-items: center;
  gap: var(--gl-space-xs);
  width: 100%;
  padding: var(--gl-space-xs) var(--gl-space-s);
  box-sizing: border-box;
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: background-color var(--gl-dur-fast) var(--gl-ease);
}
.card > li > button:only-child:hover,
.card > li > a:only-child:hover { background: var(--gl-color-surface-fill); }

/* Documentation-only extras: a body/footer pair and a responsive grid, used
   by the prose pages (rationale.md, index.md, refusals.md) to show a Card
   as a plain content tile rather than a data row. Neither is part of
   Card's props — they exist because a content tile's inner layout has no
   element of its own to hang a selector on. .card-body ul is the same idea
   applied to a checklist tile (refusals.md) instead of a single paragraph. */
.card-body {
  display: flex;
  flex-direction: column;
  gap: var(--gl-space-2xs);
  padding: var(--gl-space-m);
}
.card-body h3 { margin: 0; font: 600 var(--gl-step-0)/1.3 var(--gl-font-sans); }
.card-body p,
.card-body ul { margin: 0; color: var(--gl-color-text-secondary); font-size: var(--gl-step--1); }
.card-body ul { padding-left: 1.125em; }
.card-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gl-space-xs);
  margin-top: auto;
  padding: var(--gl-space-xs) var(--gl-space-m);
  border-top: 1px solid var(--gl-color-border-hairline);
  background: var(--gl-color-surface-fill);
}

.card[data-link] { transition: box-shadow var(--gl-dur-fast) var(--gl-ease); }
.card[data-link]:hover { box-shadow: var(--gl-shadow-sm); }
.card[data-link]:focus-within { outline: 2px solid var(--gl-color-accent); outline-offset: 2px; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--gl-space-s);
}

/* Avatar — lives here because it almost never appears outside a card or a
   row (the Loop dashboard's initials chip, Archive's file-type chip). A
   <span> is too generic for a bare selector, so it keeps one class. */
.avatar {
  --_size: 1.875rem;
  --gl-icon-start: var(--gl-icon);
  /* Sized from the box, not the initials' small font, so it follows data-size. */
  --gl-icon-style-size: calc(var(--_size) * 0.55);
  --gl-icon-style-spacing: 0;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--_size);
  height: var(--_size);
  flex: none;
  border-radius: var(--gl-radius-full);
  background: var(--gl-color-accent-soft);
  color: var(--gl-color-accent-ink);
  font-family: var(--gl-font-mono);
  font-size: var(--gl-step--2);
  font-weight: 600;
  overflow: hidden;
}
.avatar[data-square] { border-radius: var(--gl-radius-md); background: var(--gl-color-surface-fill); color: var(--gl-color-text-secondary); border: 1px solid var(--gl-color-border-hairline); }
.avatar[data-size="sm"] { --_size: 1.5rem; }
.avatar[data-size="lg"] { --_size: 2.25rem; }