File size: 29,661 Bytes
5351cc8 e4d9f48 5351cc8 56e1f2f 5351cc8 56e1f2f 5351cc8 56e1f2f c3e4cb4 56e1f2f 016c754 ea7b176 c3e4cb4 016c754 ef68ae0 016c754 56e1f2f 5351cc8 56e1f2f 5351cc8 ef68ae0 5351cc8 ef68ae0 5351cc8 ef68ae0 5351cc8 56e1f2f 5351cc8 016c754 5351cc8 | 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 | .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;
}
}
|