loopable / web /src /customer-grid /RecordDetail.css
fsanyoto's picture
Deploy AIOS web (React glide grid + FastAPI slice)
e4d9f48 verified
Raw
History Blame Contribute Delete
29.7 kB
.cg-record-backdrop {
position: fixed;
inset: 0;
z-index: var(--cg-z-drawer);
display: grid;
place-items: center;
padding: 24px;
background: rgba(25, 34, 46, 0.34);
backdrop-filter: blur(1px);
}
.cg-record-modal {
width: min(1120px, calc(100vw - 48px));
height: min(760px, calc(100vh - 48px));
min-height: 420px;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--lp-line) 82%, var(--lp-muted));
border-radius: 12px;
background: var(--lp-surface);
box-shadow:
0 30px 80px rgba(20, 30, 43, 0.24),
0 4px 18px rgba(20, 30, 43, 0.12);
color: var(--lp-ink);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
font-size: var(--lp-fs-xs);
animation: cg-record-open 150ms cubic-bezier(0.2, 0, 0, 1);
}
.cg-record-modal:focus {
outline: none;
}
/* Universal linked-record viewer: the target is the real Grid surface, not a JSON-shaped
approximation. It is deliberately wider than the record detail so filtering and multi-column
sorting have the same working room they do on the database page. */
.cg-link-grid-modal {
width: min(1320px, calc(100vw - 48px));
height: min(840px, calc(100vh - 48px));
min-height: 460px;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--lp-line) 82%, var(--lp-muted));
border-radius: 12px;
background: var(--lp-surface);
box-shadow:
0 30px 80px rgba(20, 30, 43, 0.24),
0 4px 18px rgba(20, 30, 43, 0.12);
color: var(--lp-ink);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
font-size: var(--lp-fs-xs);
animation: cg-record-open 150ms cubic-bezier(0.2, 0, 0, 1);
}
.cg-link-grid-modal:focus { outline: none; }
.cg-link-grid-head {
flex: 0 0 54px;
min-height: 54px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 0 14px 0 18px;
border-bottom: 1px solid var(--lp-line);
background: var(--lp-surface);
}
.cg-link-grid-title {
font-size: var(--lp-fs-sm);
font-weight: 650;
line-height: 1.2;
}
.cg-link-grid-sub {
margin-top: 2px;
color: var(--lp-muted);
font-size: var(--lp-fs-3xs);
}
.cg-link-grid-body {
flex: 1 1 auto;
min-width: 0;
min-height: 0;
background: var(--lp-surface);
}
.cg-link-grid-foot {
flex: 0 0 52px;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 8px 14px;
border-top: 1px solid var(--lp-line);
background: var(--lp-surface);
}
@keyframes cg-record-open {
from {
opacity: 0;
transform: translateY(8px) scale(0.992);
}
}
.cg-record-modal > .cg-detail-header {
height: 46px;
padding: 0 12px;
background: var(--lp-surface);
}
.cg-record-layout {
flex: 1 1 auto;
min-height: 0;
display: grid;
grid-template-columns: minmax(0, 1.95fr) minmax(300px, 0.95fr);
}
.cg-record-modal--single {
width: min(880px, calc(100vw - 48px));
}
.cg-record-modal--single .cg-record-layout {
grid-template-columns: minmax(0, 1fr);
}
.cg-record-main {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
.cg-record-main .cg-detail-titlewrap {
padding: 22px 28px 18px;
}
.cg-record-main .cg-detail-title {
font-size: var(--lp-fs-lg);
line-height: 1.25;
}
.cg-record-main .cg-detail-body {
min-height: 0;
padding: 4px 12px 28px;
scrollbar-gutter: stable;
}
.cg-record-main .cg-detail-section {
padding: 16px;
}
/* ==========================================================================
Wave 2026-08-02 item 2 — the record fields: no yellow, one subtle box.
==========================================================================
Owner: "kill yellow, subtle outline for all field types."
Before this, exactly one kind of field had a treatment — the editable
overlay notes, in a gold frame on a gold wash (index.css:2027) — and every
other field was bare text under a hairline. So the loudest thing in the
panel was also the least important, and the panel had two layouts: label
ABOVE a full-width textarea for notes, label BESIDE the value for the rest.
Now every field is one row on one grid: label left, a value SLOT right.
The slot carries a 1px --lp-line hairline and --lp-r-md corners, and the
fill is what says whether it is yours:
--lp-surface-2 (warm grey) read-only — an Odoo attribute, a picked
choice, a star rating you set from the table
--lp-surface (white) editable — type here
That replaces the signal the yellow wash used to carry (this stratum is
your data, not the ERP's) with one that is also an affordance: the bright
slots are the ones that respond. Interaction, not decoration, marks them.
EVERY rule here is scoped `.cg-record-main …` so it (a) outranks the
index.css base block, which is FROZEN for the other sessions this wave,
and (b) stops at the record modal — the same `.cg-detail-*` classes dress
surfaces this file does not own. Specificity, never source order: the
:focus and ::placeholder overrides below exist because their index.css
counterparts tie at (0,2,0) and (0,1,1), and a tie is decided by bundle
order — which is not a thing to bet a gold glow on. */
.cg-record-main .cg-detail-field {
min-height: 0;
align-items: flex-start;
gap: 12px;
padding: 3px 0;
/* The hairline separator is REPLACED by the box, not stacked under it —
a ruled row around a bordered slot reads as a rendering fault. */
border-bottom: none;
}
.cg-record-main .cg-detail-label {
flex: 0 0 160px;
/* Optical, not arithmetic: 7px lands the 12px label on the same line as the
13px value inside its 1px border + 5px padding. */
padding-top: 7px;
color: var(--lp-muted);
font-size: var(--lp-fs-2xs);
line-height: 1.45;
}
.cg-record-main .cg-detail-value {
min-height: 30px;
padding: 5px 9px;
border: 1px solid var(--lp-line);
border-radius: var(--lp-r-md);
background: var(--lp-surface-2);
line-height: 1.45;
}
/* ⭐ Wave-23 C8 (owner item 4 / ruling R9) — a MACHINE-OWNED field's value slot.
`--lp-machine-tint` is EXACTLY what the canvas wash (`theme.MACHINE_CELL`,
`washOf(LP_MACHINE_DEEP, 0.12)`) composites to over white, so the drawer slot
and the grid cell are one grey rather than two that were each picked to look
right on their own — the sibling-panel rule (DESIGN.md §2) applied across two
rendering technologies instead of two panes.
THE VALUE KEEPS ITS INK, deliberately, on both surfaces. The wash is the whole
signal; dimming the number as well would say the figure matters less, and an
automation-written follower count is the entire reason its column exists.
Background only — no border change, no second marker. The column menu already
says "Filled by automation" once, and DESIGN.md §4 is that the app does not
narrate itself twice. */
.cg-record-main .cg-detail-field.is-machine .cg-detail-value {
background: var(--lp-machine-tint);
}
/* A rating's marks are 18px inline SVG; on the text baseline they hang their
descender space below the line box and grow the slot past every neighbour. */
.cg-record-main .cg-detail-value .cg-stars {
vertical-align: middle;
}
/* An editable field is the same ROW as every other one now — index.css stacks
it (flex-direction: column, and a label with `flex: none`), which is what
made the notes the odd block out. */
.cg-record-main .cg-detail-field--edit {
flex-direction: row;
align-items: flex-start;
gap: 12px;
}
.cg-record-main .cg-detail-field--edit .cg-detail-label {
flex: 0 0 160px;
}
/* The value column of an editable row: the box, then its action link. */
.cg-detail-editwrap {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
}
.cg-record-main .cg-detail-textarea {
/* The owner's "big static box" was min-height: 76px. One row is the resting
size; AutoTextarea grows it from there, so an empty note is as quiet as an
empty value and a long one is readable without a scrollbar. */
min-height: 30px;
max-height: 260px;
padding: 5px 9px;
overflow-y: auto;
/* The height is measured, so a drag handle would fight the measurement: the
next keystroke re-fits and snaps back whatever was dragged. */
resize: none;
border: 1px solid var(--lp-line);
background: var(--lp-surface);
line-height: 1.45;
}
.cg-record-main .cg-detail-textarea:hover:not(:focus) {
border-color: color-mix(in srgb, var(--lp-line) 45%, var(--lp-muted));
}
.cg-record-main .cg-detail-textarea:focus {
/* Identical to the comment composer below — same modal, same gesture, so the
same mark. `box-shadow: none` is load-bearing: without it index.css:2047's
3px gold glow survives an override that only changes the border. */
outline: 2px solid var(--lp-blue-deep);
outline-offset: -1px;
border-color: transparent;
box-shadow: none;
}
.cg-record-main .cg-detail-textarea::placeholder {
/* --lp-muted is the palette's body-safe grey (4.83:1). The stock #a2a2ac was
picked against the gold wash and reads as 2.7:1 on white — and "Add
notes…" is an instruction, not decoration. */
color: var(--lp-muted);
opacity: 1;
}
/* A checkbox IS its own control; boxing it would frame a 15px square in a
30px slot and say nothing. It only needs to sit on the label's line. */
.cg-record-main .cg-detail-checkbox {
margin-top: 7px;
}
/* ==========================================================================
Wave 14 item 9 — an editor for EVERY user-created field type.
==========================================================================
Until now one type was editable here (notes) and the other eight read. The
affordance law item 2 established is what makes this scale without a legend:
--lp-surface-2 (warm grey) read-only
--lp-surface (white) yours to change
so `--live` is the ONLY thing that says "this responds", across nine row
shapes. It is deliberately the same white the textarea already used, not a
new colour: adding a third fill would turn a two-state signal into a puzzle.
ONE BOX PER ROW. The controls are borderless and transparent INSIDE the
slot rather than bordered themselves — a bordered input inside a bordered
slot draws two rectangles 1px apart, which reads as a rendering fault, and
it would also break the single label-left/value-right grid every other row
sits on. The focus ring therefore lands on the SLOT (`:focus-within`) and
the control's own outline is suppressed; the ring is the same 2px inset
blue as the textarea and the comment composer, because it is the same
gesture in the same modal.
⚠ `font: inherit` is NOT used on these controls. The `font` shorthand
RESETS `font-variant-numeric`, which `.cg-detail-value` sets to tabular-nums
(index.css:2013) — so the shorthand would silently un-align every number in
the panel. The longhands are spelled out instead. */
.cg-record-main .cg-detail-value--live {
background: var(--lp-surface);
}
.cg-record-main .cg-detail-value--live:hover:not(:focus-within) {
border-color: color-mix(in srgb, var(--lp-line) 45%, var(--lp-muted));
}
.cg-record-main .cg-detail-value--live:focus-within {
outline: 2px solid var(--lp-blue-deep);
outline-offset: -1px;
border-color: transparent;
}
/* The slot as a ROW: an avatar, a currency mark or a hint sits beside its
control instead of above it. */
.cg-record-main .cg-detail-pickslot {
display: flex;
align-items: center;
gap: 7px;
}
.cg-record-main .cg-detail-select,
.cg-record-main .cg-detail-input {
flex: 1 1 auto;
min-width: 0;
width: 100%;
padding: 0;
border: 0;
background: transparent;
color: var(--lp-ink);
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: 1.45;
}
/* The ring is on the slot; a second one inside it would draw a rectangle
around a rectangle. */
.cg-record-main .cg-detail-select:focus,
.cg-record-main .cg-detail-input:focus {
outline: none;
}
.cg-record-main .cg-detail-select {
/* The native arrow is kept — it is the only thing that says "pick" once the
border has been handed to the slot. */
cursor: pointer;
margin-left: -2px;
}
.cg-record-main .cg-detail-input--num {
text-align: right;
}
/* Spinners removed, and not for looks: this panel SCROLLS, and a wheel event
over a focused number input changes the value in Chrome. A currency figure
silently edited by scrolling past it is data loss with no gesture behind it.
The right-aligned tabular figure and the unit mark carry the type. */
.cg-record-main .cg-detail-input--num::-webkit-outer-spin-button,
.cg-record-main .cg-detail-input--num::-webkit-inner-spin-button {
appearance: none;
margin: 0;
}
.cg-record-main .cg-detail-input--num {
-moz-appearance: textfield;
appearance: textfield;
}
.cg-record-main .cg-detail-unit,
.cg-record-main .cg-detail-hint {
flex: 0 0 auto;
color: var(--lp-muted);
font-size: var(--lp-fs-2xs);
}
.cg-record-main .cg-detail-hint {
font-style: italic;
}
.cg-record-main .cg-detail-nochoice {
color: var(--lp-muted);
font-size: var(--lp-fs-2xs);
}
/* Multiselect: a chip row, not a `<select multiple>`. The native control needs
ctrl-click for a second value and paints a four-row scrollbox for a
three-choice field; a chip says its own state and takes one click. */
.cg-record-main .cg-detail-chips {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.cg-record-main .cg-detail-chip {
height: 22px;
padding: 0 9px;
border: 1px solid var(--lp-line);
border-radius: var(--lp-r-pill);
background: var(--lp-surface-2);
color: var(--lp-muted);
font-family: inherit;
font-size: var(--lp-fs-2xs);
line-height: 1;
white-space: nowrap;
cursor: pointer;
}
.cg-record-main .cg-detail-chip:hover {
border-color: var(--lp-blue-deep);
}
/* Tint fills, `-deep` on the text — the brand rule. The base pastel is not a
readable ink at 12px. */
.cg-record-main .cg-detail-chip.is-on {
border-color: var(--lp-blue-deep);
background: var(--lp-blue-tint);
color: var(--lp-blue-deep);
font-weight: 600;
}
.cg-record-main .cg-detail-chip:focus-visible {
outline: 2px solid var(--lp-blue-deep);
outline-offset: 1px;
}
/* ⭐ Wave-19 R7 — the IMAGE slot: the picture, then its reference and controls.
The thumbnail is `object-fit: contain` for the reason the grid cell is: a
product photo cropped to a square loses the thing being sold. Letterboxing
inside a bounded frame is the honest fit for a catalogue picture. */
.cg-record-main .cg-detail-imageslot {
display: flex;
align-items: flex-start;
gap: 10px;
}
.cg-record-main .cg-detail-image,
.cg-record-main .cg-detail-imageempty {
flex: 0 0 auto;
width: 72px;
height: 72px;
border: 1px solid var(--lp-line);
border-radius: var(--lp-r-sm);
background: var(--lp-surface-2);
object-fit: contain;
}
.cg-record-main .cg-detail-imagemeta {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 5px;
min-width: 0;
}
/* The reference itself, in the mono face: it is an identifier (a SKU code, a
slug, a minted id), and identifiers are read character by character. The
stack is spelled out rather than tokenised because there is no `--lp-mono`
token — the three existing monospace sites in index.css all spell it too, and
C6 says NO new tokens this wave. */
.cg-record-main .cg-detail-imageref {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--lp-muted);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: var(--lp-fs-2xs);
}
.cg-record-main .cg-detail-imageacts {
display: inline-flex;
gap: 6px;
}
/* The `<label>` and the `<button>` are one control visually — the file input
inside the label is hidden, which is the only way to style a file picker. */
.cg-record-main .cg-detail-imagebtn {
display: inline-flex;
align-items: center;
height: 22px;
padding: 0 9px;
border: 1px solid var(--lp-line);
border-radius: var(--lp-r-pill);
background: var(--lp-surface-2);
color: var(--lp-muted);
font-family: inherit;
font-size: var(--lp-fs-2xs);
line-height: 1;
white-space: nowrap;
cursor: pointer;
}
.cg-record-main .cg-detail-imagebtn:hover {
border-color: var(--lp-blue-deep);
color: var(--lp-blue-deep);
}
.cg-record-main .cg-detail-imagebtn input[type="file"] {
display: none;
}
.cg-record-main .cg-detail-imageerr {
color: var(--lp-red-deep);
font-size: var(--lp-fs-2xs);
}
/* ⭐ Wave-23 C7 (owner item 5) — a `json` field's row: the same compact preview the grid cell
paints, plus the door to the viewer. Reuses `.cg-detail-imagebtn` rather than minting a
second quiet-button shape (DESIGN.md §4) — the two rows are doing the same job, showing a
value too big for a slot and offering the surface that owns it. */
.cg-record-main .cg-detail-jsonslot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
/* ⚠ `min-width: 0` is what lets the ellipsis happen at all: a flex item's default
`min-width: auto` refuses to shrink below its content, so the preview would push the Open
button out of the panel instead of truncating. Monospace because the preview is a fragment
of a document — `{handle: "royalimports"}` reads as code, and setting it in Inter beside the
viewer's monospace tree would make one value look like two different things. */
.cg-record-main .cg-detail-jsonpreview {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: var(--lp-fs-2xs);
color: var(--lp-ink);
}
/* Rating: the stars ARE the picker. Clicking the lit star clears the value —
without it a rating could be changed here but never removed. */
.cg-record-main .cg-detail-stars {
display: inline-flex;
align-items: center;
gap: 1px;
}
.cg-record-main .cg-detail-star {
display: grid;
place-items: center;
padding: 0 1px;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
line-height: 0;
}
.cg-record-main .cg-detail-star:focus-visible {
outline: 2px solid var(--lp-blue-deep);
outline-offset: 1px;
border-radius: var(--lp-r-sm);
}
/* ==========================================================================
Wave 14 item 11 (C-AVATAR) — the person, as a face.
==========================================================================
R6: cells show the disc alone; here it sits BESIDE the name, because two
initials at 20px identify nobody by themselves. The initials pastel is
`pickTint`'s hash — the same one the option pills use — so one person is one
colour on every surface, and it is the DESIGNED state rather than the error
state: every deployment renders it until a photo is uploaded. */
.cg-record-main .cg-detail-avatar {
flex: 0 0 auto;
width: 20px;
height: 20px;
display: inline-grid;
place-items: center;
border-radius: 50%;
object-fit: cover;
font-size: var(--lp-fs-4xs);
font-weight: 700;
letter-spacing: 0.01em;
line-height: 1;
}
/* ==========================================================================
Wave 2026-08-02 item 5 (C-LAYOUT) — the six-dot reorder handle.
==========================================================================
Hover-revealed, Airtable's own behaviour and the reason it is right here:
this panel carries ~26 fields, and 26 permanently-visible grips would put a
dotted column down the left edge louder than any value in the record. The
handle is an affordance for a gesture, not information.
It keeps its 16px gutter at rest (`opacity`, never `display`), so revealing
it cannot reflow the row under the pointer that is reaching for it — and it
stays in the tab order at all times, which is what makes the ArrowUp/Down
fallback reachable without a mouse.
⚠ Known gap, booked rather than papered over: HTML5 drag-and-drop does not
fire for touch. On a touch device there is no hover, so the grip stays
invisible and the gesture is simply absent — which is the honest
degradation. A pointer-events reorder is the fix if it is ever asked for. */
.cg-detail-grip {
flex: 0 0 16px;
height: 22px;
margin-top: 5px;
display: grid;
place-items: center;
padding: 0;
border: 0;
border-radius: var(--lp-r-sm);
background: transparent;
color: var(--lp-muted);
cursor: grab;
opacity: 0;
transition: opacity 90ms ease;
}
.cg-detail-field:hover .cg-detail-grip,
.cg-detail-grip:focus-visible {
opacity: 1;
}
.cg-detail-grip:focus-visible {
outline: 2px solid var(--lp-blue-deep);
outline-offset: -1px;
}
.cg-detail-grip:active {
cursor: grabbing;
}
/* The row under the pointer. It is where the dragged field LANDS (it takes
this slot), so the whole row is marked rather than an insertion line
between two — an edge cannot say which side you are on. */
.cg-detail-field.is-dropzone {
border-radius: var(--lp-r-md);
background: var(--lp-blue-tint);
box-shadow: inset 0 0 0 1px var(--lp-blue-deep);
}
.cg-detail-field.is-dragging {
opacity: 0.4;
}
/* ==========================================================================
Wave 2026-08-02 item 13 (C-EMBED) — Fields | Insights.
==========================================================================
An underline tab row, not a segmented pill: these are two READINGS of the
same record, and a pill group reads as a mode switch that changes what the
panel is for. Sits under the title, above the scrolling body, so switching
never moves the customer's name. */
.cg-record-tabs {
flex: 0 0 auto;
display: flex;
gap: 2px;
padding: 0 28px;
border-bottom: 1px solid var(--lp-line);
}
.cg-record-tab {
padding: 7px 10px 8px;
border: 0;
/* The 1px underline lives on the button so the active mark sits ON the row's
hairline rather than 1px above or below it. */
border-bottom: 2px solid transparent;
margin-bottom: -1px;
background: transparent;
color: var(--lp-muted);
font: inherit;
font-size: var(--lp-fs-2xs);
font-weight: 600;
cursor: pointer;
}
.cg-record-tab:hover {
color: var(--lp-ink);
}
.cg-record-tab.is-on {
border-bottom-color: var(--lp-blue-solid);
color: var(--lp-ink);
}
.cg-record-tab:focus-visible {
outline: 2px solid var(--lp-blue-deep);
outline-offset: -2px;
}
.cg-record-insights {
padding: 12px 16px 24px;
}
@media (max-width: 760px) {
.cg-record-tabs {
padding-inline: 18px;
}
.cg-record-insights {
padding-inline: 4px;
}
}
.cg-record-comments {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
border-left: 1px solid var(--lp-line);
background: var(--lp-wash);
}
.cg-record-comments-head {
flex: 0 0 52px;
display: flex;
align-items: center;
gap: 8px;
padding: 0 18px;
border-bottom: 1px solid var(--lp-line);
background: var(--lp-surface);
}
.cg-record-comments-head h2 {
margin: 0;
font-size: var(--lp-fs-sm);
font-weight: 650;
}
.cg-record-comments-head span {
min-width: 20px;
padding: 1px 6px;
border-radius: var(--lp-r-pill);
background: var(--lp-surface-2);
color: var(--lp-muted);
font-size: var(--lp-fs-3xs);
text-align: center;
}
.cg-record-comment-feed {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
padding: 12px 16px;
}
.cg-record-comment-state {
padding: 26px 10px;
color: var(--lp-muted);
font-size: var(--lp-fs-2xs);
line-height: 1.5;
text-align: center;
}
.cg-record-comment {
display: grid;
grid-template-columns: 30px minmax(0, 1fr);
gap: 9px;
padding: 10px 0 13px;
}
.cg-record-comment + .cg-record-comment {
border-top: 1px solid var(--lp-line);
}
.cg-record-comment-avatar {
width: 30px;
height: 30px;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--lp-blue-tint);
color: var(--lp-blue-solid);
font-size: var(--lp-fs-4xs);
font-weight: 700;
letter-spacing: 0.01em;
}
.cg-record-comment-content {
min-width: 0;
}
.cg-record-comment-meta {
display: flex;
align-items: baseline;
gap: 6px;
}
.cg-record-comment-meta strong {
min-width: 0;
overflow: hidden;
color: var(--lp-ink);
font-size: var(--lp-fs-2xs);
font-weight: 650;
text-overflow: ellipsis;
white-space: nowrap;
}
.cg-record-comment-meta time {
flex: 0 0 auto;
color: var(--lp-muted);
font-size: var(--lp-fs-4xs);
}
.cg-record-comment-delete {
flex: 0 0 auto;
width: 20px;
height: 20px;
margin-left: auto;
border: 0;
border-radius: var(--lp-r-sm);
background: transparent;
color: var(--lp-muted);
cursor: pointer;
}
.cg-record-comment-delete:hover {
background: var(--lp-red-tint);
color: var(--lp-red-deep);
}
.cg-record-comment-content p {
margin: 4px 0 0;
color: var(--lp-ink);
font-size: var(--lp-fs-2xs);
line-height: 1.48;
overflow-wrap: anywhere;
white-space: pre-wrap;
}
.cg-record-comment-error {
margin: 8px 0;
padding: 8px 10px;
border: 1px solid var(--lp-red);
border-radius: var(--lp-r-md);
background: var(--lp-red-tint);
color: var(--lp-red-deep);
font-size: var(--lp-fs-3xs);
line-height: 1.4;
}
.cg-record-comment-compose {
flex: 0 0 auto;
padding: 12px 14px 14px;
border-top: 1px solid var(--lp-line);
background: var(--lp-surface);
}
.cg-record-comment-compose label {
display: block;
margin-bottom: 6px;
color: var(--lp-ink);
font-size: var(--lp-fs-2xs);
font-weight: 600;
}
.cg-record-comment-compose textarea {
width: 100%;
min-height: 78px;
max-height: 180px;
resize: vertical;
padding: 9px 10px;
border: 1px solid var(--lp-line);
border-radius: var(--lp-r-md);
background: var(--lp-surface);
color: var(--lp-ink);
font: inherit;
font-size: var(--lp-fs-2xs);
line-height: 1.45;
}
.cg-record-comment-compose textarea:focus {
outline: 2px solid var(--lp-blue-deep);
outline-offset: -1px;
border-color: transparent;
}
.cg-record-comment-compose-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 7px;
}
.cg-record-comment-compose-foot span {
color: var(--lp-muted);
font-size: var(--lp-fs-4xs);
}
.cg-record-comment-compose-foot button {
min-width: 76px;
height: 28px;
border: 1px solid var(--lp-blue-solid);
border-radius: var(--lp-r-md);
background: var(--lp-blue-solid);
color: white;
font: inherit;
font-size: var(--lp-fs-2xs);
font-weight: 600;
cursor: pointer;
}
.cg-record-comment-compose-foot button:disabled {
opacity: 0.48;
cursor: default;
}
@media (max-width: 760px) {
.cg-record-backdrop {
padding: 0;
}
.cg-record-modal,
.cg-record-modal--single {
width: 100vw;
height: 100dvh;
min-height: 0;
border: 0;
border-radius: 0;
}
.cg-record-layout {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: minmax(220px, 1.2fr) minmax(250px, 0.8fr);
}
.cg-record-comments {
border-top: 1px solid var(--lp-line);
border-left: 0;
}
.cg-record-main .cg-detail-titlewrap {
padding: 16px 18px 14px;
}
.cg-record-main .cg-detail-body {
padding-inline: 2px;
}
/* Both selectors, because the editable row's label is set at (0,3,0) above:
narrowing only the read-only one would step the two label columns apart at
exactly the width where the misalignment is most visible. */
.cg-record-main .cg-detail-label,
.cg-record-main .cg-detail-field--edit .cg-detail-label {
flex-basis: 120px;
}
}
/* ==========================================================================
Wave 14 R7 — one line, no wraps: the record modal's button rows.
==========================================================================
Audited every row of controls this session renders. Two were real risks and
the rest were already sound, which is worth recording so the next sweep does
not re-derive it:
· the header (‹ › · "Record 3 of 1,234" · ×) — the buttons are fixed
28px squares, but the POSITION is variable text in a 44/46px fixed-height
row. At a narrow width it wrapped to two lines and pushed itself out of
the header it lives in.
· the comment composer's foot — a character counter opposite a Post
button under `space-between`; the counter wrapped before the button
shrank, which moved the button off its baseline.
· Documents rows were ALREADY correct (index.css: name ellipsises,
meta and delete are `flex: 0 0 auto`) — left alone.
· the tab row, the chip rows and the star row are new this wave and were
built nowrap; the declarations below are what make that a rule rather
than a coincidence of the current labels.
`nowrap` is the assertion. A label that outgrows its space now overflows
VISIBLY instead of quietly reflowing — the failure you can see beats the one
you cannot. */
.cg-record-modal .cg-detail-pos,
.cg-record-tab,
.cg-record-main .cg-detail-chip,
.cg-record-comment-compose-foot button {
white-space: nowrap;
}
.cg-record-comment-compose-foot span {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.cg-record-comment-compose-foot button {
flex: 0 0 auto;
}
@media (prefers-reduced-motion: reduce) {
.cg-record-modal {
animation: none;
}
}