@macrostrat/column-views 3.11.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/dist/age-model/data.cjs +57 -0
  3. package/dist/age-model/data.cjs.map +1 -0
  4. package/dist/age-model/data.d.ts +29 -0
  5. package/dist/age-model/data.js +57 -0
  6. package/dist/age-model/data.js.map +1 -0
  7. package/dist/age-model/details.cjs +261 -0
  8. package/dist/age-model/details.cjs.map +1 -0
  9. package/dist/age-model/details.d.ts +39 -0
  10. package/dist/age-model/details.js +258 -0
  11. package/dist/age-model/details.js.map +1 -0
  12. package/dist/age-model/details.module.sass.cjs +42 -0
  13. package/dist/age-model/details.module.sass.cjs.map +1 -0
  14. package/dist/age-model/details.module.sass.js +40 -0
  15. package/dist/age-model/details.module.sass.js.map +1 -0
  16. package/dist/age-model/index.d.ts +5 -0
  17. package/dist/age-model/legacy.cjs +28 -0
  18. package/dist/age-model/legacy.cjs.map +1 -0
  19. package/dist/age-model/legacy.d.ts +6 -0
  20. package/dist/age-model/legacy.js +26 -0
  21. package/dist/age-model/legacy.js.map +1 -0
  22. package/dist/age-model/surfaces.cjs +355 -0
  23. package/dist/age-model/surfaces.cjs.map +1 -0
  24. package/dist/age-model/surfaces.d.ts +68 -0
  25. package/dist/age-model/surfaces.js +352 -0
  26. package/dist/age-model/surfaces.js.map +1 -0
  27. package/dist/age-model/surfaces.module.sass.cjs +45 -0
  28. package/dist/age-model/surfaces.module.sass.cjs.map +1 -0
  29. package/dist/age-model/surfaces.module.sass.js +43 -0
  30. package/dist/age-model/surfaces.module.sass.js.map +1 -0
  31. package/dist/age-model/types.cjs +229 -0
  32. package/dist/age-model/types.cjs.map +1 -0
  33. package/dist/age-model/types.d.ts +112 -0
  34. package/dist/age-model/types.js +229 -0
  35. package/dist/age-model/types.js.map +1 -0
  36. package/dist/column-views.css +389 -57
  37. package/dist/column.cjs +20 -13
  38. package/dist/column.cjs.map +1 -1
  39. package/dist/column.d.ts +21 -6
  40. package/dist/column.js +21 -14
  41. package/dist/column.js.map +1 -1
  42. package/dist/column.module.sass.cjs +19 -15
  43. package/dist/column.module.sass.cjs.map +1 -1
  44. package/dist/column.module.sass.js +19 -15
  45. package/dist/column.module.sass.js.map +1 -1
  46. package/dist/data-provider/store.cjs +34 -1
  47. package/dist/data-provider/store.cjs.map +1 -1
  48. package/dist/data-provider/store.d.ts +5 -0
  49. package/dist/data-provider/store.js +35 -2
  50. package/dist/data-provider/store.js.map +1 -1
  51. package/dist/index.cjs +54 -3
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.ts +3 -1
  54. package/dist/index.js +54 -3
  55. package/dist/index.js.map +1 -1
  56. package/dist/notes.cjs +11 -2
  57. package/dist/notes.cjs.map +1 -1
  58. package/dist/notes.d.ts +11 -1
  59. package/dist/notes.js +11 -2
  60. package/dist/notes.js.map +1 -1
  61. package/dist/prepare-units/composite-scale.cjs +18 -16
  62. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  63. package/dist/prepare-units/composite-scale.js +18 -16
  64. package/dist/prepare-units/composite-scale.js.map +1 -1
  65. package/dist/prepare-units/density.cjs +83 -0
  66. package/dist/prepare-units/density.cjs.map +1 -0
  67. package/dist/prepare-units/density.d.ts +92 -0
  68. package/dist/prepare-units/density.js +83 -0
  69. package/dist/prepare-units/density.js.map +1 -0
  70. package/dist/prepare-units/index.cjs +15 -7
  71. package/dist/prepare-units/index.cjs.map +1 -1
  72. package/dist/prepare-units/index.d.ts +1 -0
  73. package/dist/prepare-units/index.js +15 -7
  74. package/dist/prepare-units/index.js.map +1 -1
  75. package/dist/prepare-units/types.cjs.map +1 -1
  76. package/dist/prepare-units/types.d.ts +34 -13
  77. package/dist/prepare-units/types.js.map +1 -1
  78. package/dist/section.cjs +108 -24
  79. package/dist/section.cjs.map +1 -1
  80. package/dist/section.d.ts +66 -6
  81. package/dist/section.js +109 -25
  82. package/dist/section.js.map +1 -1
  83. package/dist/timescale-zoom.cjs +191 -0
  84. package/dist/timescale-zoom.cjs.map +1 -0
  85. package/dist/timescale-zoom.d.ts +74 -0
  86. package/dist/timescale-zoom.js +191 -0
  87. package/dist/timescale-zoom.js.map +1 -0
  88. package/dist/unit-details/age-range.cjs +1 -0
  89. package/dist/unit-details/age-range.cjs.map +1 -1
  90. package/dist/unit-details/age-range.d.ts +5 -1
  91. package/dist/unit-details/age-range.js +1 -0
  92. package/dist/unit-details/age-range.js.map +1 -1
  93. package/dist/units/composite.cjs +4 -1
  94. package/dist/units/composite.cjs.map +1 -1
  95. package/dist/units/composite.d.ts +2 -0
  96. package/dist/units/composite.js +4 -1
  97. package/dist/units/composite.js.map +1 -1
  98. package/package.json +7 -7
  99. package/src/age-model/data.ts +98 -0
  100. package/src/age-model/details.module.sass +108 -0
  101. package/src/age-model/details.ts +333 -0
  102. package/src/age-model/index.ts +5 -0
  103. package/src/age-model/legacy.ts +30 -0
  104. package/src/age-model/surfaces.module.sass +168 -0
  105. package/src/age-model/surfaces.ts +568 -0
  106. package/src/age-model/types.ts +392 -0
  107. package/src/column.module.sass +59 -8
  108. package/src/column.ts +49 -23
  109. package/src/data-provider/store.ts +50 -1
  110. package/src/index.ts +5 -1
  111. package/src/notes.ts +20 -2
  112. package/src/prepare-units/composite-scale.ts +34 -31
  113. package/src/prepare-units/density.ts +197 -0
  114. package/src/prepare-units/index.ts +25 -6
  115. package/src/prepare-units/types.ts +34 -13
  116. package/src/section.ts +219 -33
  117. package/src/timescale-zoom.ts +341 -0
  118. package/src/unit-details/age-range.ts +3 -3
  119. package/src/units/composite.ts +11 -0
  120. package/dist/age-model-overlay.cjs +0 -50
  121. package/dist/age-model-overlay.cjs.map +0 -1
  122. package/dist/age-model-overlay.d.ts +0 -24
  123. package/dist/age-model-overlay.js +0 -48
  124. package/dist/age-model-overlay.js.map +0 -1
  125. package/dist/age-model-overlay.module.sass.cjs +0 -12
  126. package/dist/age-model-overlay.module.sass.cjs.map +0 -1
  127. package/dist/age-model-overlay.module.sass.js +0 -11
  128. package/dist/age-model-overlay.module.sass.js.map +0 -1
  129. package/src/age-model-overlay.module.sass +0 -11
  130. package/src/age-model-overlay.ts +0 -96
@@ -0,0 +1,333 @@
1
+ /** The inspector for a selected surface, and the small tags and legend that
2
+ * explain surface statuses and types. */
3
+ import hyper from "@macrostrat/hyper";
4
+ import classNames from "classnames";
5
+ import { type ReactNode, useMemo } from "react";
6
+ import { Button, Tag } from "@blueprintjs/core";
7
+ import {
8
+ DataField,
9
+ Identifier,
10
+ type IntervalShort,
11
+ IntervalTag,
12
+ UnitIdentifier,
13
+ Value,
14
+ } from "@macrostrat/data-components";
15
+ import { useMacrostratDefs } from "@macrostrat/data-provider";
16
+ import type { UnitLong } from "@macrostrat/api-types";
17
+ import { useColumnUnitsMap } from "../data-provider";
18
+ import { AgeLabel, Proportion } from "../unit-details/age-range";
19
+ import { formatRange } from "../unit-details/utils";
20
+ import { ReferencesField } from "../unit-details/panel";
21
+ import { surfaceClasses } from "./surfaces";
22
+ import surfaceStyles from "./surfaces.module.sass";
23
+ import styles from "./details.module.sass";
24
+ import {
25
+ type BoundaryType,
26
+ type ColumnSurface,
27
+ type SurfaceStatus,
28
+ formatProportion,
29
+ surfaceStatuses,
30
+ surfaceStatusDescriptions,
31
+ surfaceStatusLabels,
32
+ } from "./types";
33
+
34
+ const h = hyper.styled({ ...surfaceStyles, ...styles });
35
+
36
+ export interface SurfaceDetailsPanelProps {
37
+ /** The surface to inspect; `null` renders the panel's empty state */
38
+ surface: ColumnSurface | null;
39
+ onClose?: () => void;
40
+ /** Called when a unit above or below the surface is clicked */
41
+ onSelectUnit?: (unitID: number) => void;
42
+ /** Extra controls for the header */
43
+ actions?: ReactNode;
44
+ /** Label for the measured-position field ("Height" or "Depth") */
45
+ positionLabel?: string;
46
+ className?: string;
47
+ }
48
+
49
+ /** Details of one surface: its age, calibration, the units it separates, and
50
+ * provenance. A companion to `UnitDetailsPanel`. */
51
+ export function SurfaceDetailsPanel(props: SurfaceDetailsPanelProps) {
52
+ const {
53
+ surface,
54
+ onClose,
55
+ onSelectUnit,
56
+ actions,
57
+ positionLabel = "Position",
58
+ className,
59
+ } = props;
60
+
61
+ // A selection that hasn't been made yet is an ordinary state for a panel
62
+ // bound to one, not a caller error
63
+ if (surface == null) {
64
+ return h(
65
+ "div.surface-details-panel.no-surface",
66
+ { className },
67
+ h("p.placeholder", "No surface selected."),
68
+ );
69
+ }
70
+
71
+ let closeButton: ReactNode = null;
72
+ if (onClose != null) {
73
+ closeButton = h(Button, {
74
+ icon: "cross",
75
+ variant: "minimal",
76
+ size: "small",
77
+ onClick: onClose,
78
+ "aria-label": "Close",
79
+ });
80
+ }
81
+
82
+ let positionField: ReactNode = null;
83
+ if (surface.position != null) {
84
+ positionField = h(DataField, {
85
+ label: positionLabel,
86
+ value: surface.position.toLocaleString("en-US", {
87
+ maximumFractionDigits: 2,
88
+ }),
89
+ unit: "m",
90
+ });
91
+ }
92
+
93
+ let sectionField: ReactNode = null;
94
+ if (surface.section_id != null) {
95
+ sectionField = h(
96
+ DataField,
97
+ { label: "Section" },
98
+ h(Identifier, { id: surface.section_id }),
99
+ );
100
+ }
101
+
102
+ let refsField: ReactNode = null;
103
+ if (surface.ref_id != null) {
104
+ refsField = h(ReferencesField, { refs: [surface.ref_id] });
105
+ }
106
+
107
+ return h(
108
+ "div.surface-details-panel",
109
+ { className: classNames(className, surfaceClasses(surface)) },
110
+ [
111
+ h("div.surface-details-header", [
112
+ h("h3.surface-details-title", [
113
+ "Surface ",
114
+ h(SurfaceIdentifier, { id: surface.id }),
115
+ ]),
116
+ h("div.surface-details-tags", [
117
+ h(SurfaceStatusTag, {
118
+ status: surface.status,
119
+ inferred: surface.statusInferred,
120
+ }),
121
+ h(SurfaceTypeTag, { type: surface.type }),
122
+ ]),
123
+ h("div.spacer"),
124
+ actions,
125
+ closeButton,
126
+ ]),
127
+ h("div.surface-details-content", [
128
+ h(DataField, { label: "Model age" }, h(AgeLabel, { age: surface.age })),
129
+ h(SurfaceCalibrationField, { surface }),
130
+ positionField,
131
+ h(SurfaceUnitsField, {
132
+ label: "Unit above",
133
+ unitIDs: surface.unitsAbove,
134
+ onSelectUnit,
135
+ }),
136
+ h(SurfaceUnitsField, {
137
+ label: "Unit below",
138
+ unitIDs: surface.unitsBelow,
139
+ onSelectUnit,
140
+ }),
141
+ sectionField,
142
+ refsField,
143
+ ]),
144
+ ],
145
+ );
146
+ }
147
+
148
+ function SurfaceIdentifier({ id }: { id: ColumnSurface["id"] }) {
149
+ if (typeof id === "number") {
150
+ return h(Identifier, { id });
151
+ }
152
+ return h("code.surface-id", String(id));
153
+ }
154
+
155
+ /** The calibration interval as a colored tag, with the surface's position in
156
+ * it: "Calymmian (1600–1400 Ma), 85% above base". */
157
+ function SurfaceCalibrationField({ surface }: { surface: ColumnSurface }) {
158
+ const { calibration, proportion } = surface;
159
+ const ids = useMemo(() => {
160
+ if (calibration == null) return [];
161
+ return [calibration.id];
162
+ }, [calibration?.id]);
163
+ // Interval colors come from the definitions table; the age model itself
164
+ // doesn't carry them
165
+ const intervalMap = useMacrostratDefs("intervals", ids, null);
166
+
167
+ if (calibration == null) {
168
+ return h(DataField, {
169
+ label: "Calibration",
170
+ value: "None",
171
+ className: "no-calibration",
172
+ });
173
+ }
174
+
175
+ const def = intervalMap?.get(calibration.id);
176
+ const interval: IntervalShort = {
177
+ id: calibration.id,
178
+ name: calibration.name,
179
+ b_age: calibration.b_age,
180
+ t_age: calibration.t_age,
181
+ color: def?.color,
182
+ rank: def?.rank,
183
+ };
184
+
185
+ return h(DataField, { label: "Calibration" }, [
186
+ h(
187
+ "div.calibration-interval",
188
+ h(IntervalTag, {
189
+ interval,
190
+ prefix: h(Proportion, { value: proportion }),
191
+ }),
192
+ ),
193
+ ]);
194
+ }
195
+
196
+ function SurfaceUnitsField({
197
+ label,
198
+ unitIDs,
199
+ onSelectUnit,
200
+ }: {
201
+ label: string;
202
+ unitIDs: number[];
203
+ onSelectUnit?: (unitID: number) => void;
204
+ }) {
205
+ const unitsMap = useColumnUnitsMap();
206
+ if (unitIDs.length === 0) {
207
+ return h(DataField, { label, value: "None", className: "no-units" });
208
+ }
209
+ return h(
210
+ DataField,
211
+ { label, className: "surface-units-field" },
212
+ h(
213
+ "ul.surface-units-list",
214
+ unitIDs.map((unitID) => {
215
+ const unit = unitsMap?.get(unitID) as UnitLong | undefined;
216
+ let onClick: (() => void) | undefined = undefined;
217
+ if (onSelectUnit != null) {
218
+ onClick = () => onSelectUnit(unitID);
219
+ }
220
+ return h(
221
+ "li",
222
+ { key: unitID },
223
+ h(UnitIdentifier, {
224
+ unitID,
225
+ colID: unit?.col_id,
226
+ name: unit?.unit_name,
227
+ onClick,
228
+ }),
229
+ );
230
+ }),
231
+ ),
232
+ );
233
+ }
234
+
235
+ export function SurfaceStatusTag({
236
+ status,
237
+ inferred = false,
238
+ className,
239
+ }: {
240
+ status: SurfaceStatus;
241
+ /** The status was inferred rather than recorded (see
242
+ * `inferTiePointStatuses`); the tag says so. */
243
+ inferred?: boolean;
244
+ className?: string;
245
+ }) {
246
+ let label: ReactNode = surfaceStatusLabels[status] ?? status;
247
+ let title = surfaceStatusDescriptions[status];
248
+ if (inferred) {
249
+ label = [label, h("span.inferred-marker", "*")];
250
+ title = INFERRED_STATUS_DESCRIPTION;
251
+ }
252
+
253
+ return h(
254
+ Tag,
255
+ {
256
+ variant: "minimal",
257
+ className: classNames(
258
+ "surface-tag",
259
+ className,
260
+ surfaceClasses({ status }),
261
+ { inferred },
262
+ ),
263
+ title,
264
+ },
265
+ label,
266
+ );
267
+ }
268
+
269
+ const INFERRED_STATUS_DESCRIPTION =
270
+ "Recorded as modeled, but this surface cannot have been interpolated — it " +
271
+ "sits on the base or top of its interval, or is the edge of a gap-bound " +
272
+ "package — so it constrains the age model rather than falling out of it";
273
+
274
+ export function SurfaceTypeTag({
275
+ type,
276
+ className,
277
+ }: {
278
+ type: BoundaryType | null | undefined;
279
+ className?: string;
280
+ }) {
281
+ if (type == null || type === "") return null;
282
+ return h(
283
+ Tag,
284
+ {
285
+ variant: "minimal",
286
+ className: classNames("surface-tag", "surface-type-tag", className),
287
+ },
288
+ type,
289
+ );
290
+ }
291
+
292
+ export interface SurfaceStatusLegendProps {
293
+ statuses?: SurfaceStatus[];
294
+ showDescriptions?: boolean;
295
+ className?: string;
296
+ }
297
+
298
+ /** A legend of surface line styles, one entry per boundary status. */
299
+ export function SurfaceStatusLegend(props: SurfaceStatusLegendProps) {
300
+ const {
301
+ statuses = surfaceStatuses,
302
+ showDescriptions = false,
303
+ className,
304
+ } = props;
305
+ return h(
306
+ "ul.surface-status-legend",
307
+ {
308
+ className: classNames(className, {
309
+ "with-descriptions": showDescriptions,
310
+ }),
311
+ },
312
+ statuses.map((status) => {
313
+ let description: ReactNode = null;
314
+ if (showDescriptions) {
315
+ description = h(
316
+ "div.legend-description",
317
+ surfaceStatusDescriptions[status],
318
+ );
319
+ }
320
+ return h(
321
+ "li.legend-item",
322
+ { key: status, className: surfaceClasses({ status }) },
323
+ [
324
+ h("div.surface-label-marker"),
325
+ h("div.legend-text", [
326
+ h("div.legend-label", surfaceStatusLabels[status]),
327
+ description,
328
+ ]),
329
+ ],
330
+ );
331
+ }),
332
+ );
333
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./types";
2
+ export * from "./data";
3
+ export * from "./surfaces";
4
+ export * from "./details";
5
+ export * from "./legacy";
@@ -0,0 +1,30 @@
1
+ /** Earlier, in-development overlays, kept so existing consumers keep working.
2
+ * Both are now thin wrappers over `ColumnSurfaces`. */
3
+ import h from "@macrostrat/hyper";
4
+ import { useMemo } from "react";
5
+ import type { UnitLong } from "@macrostrat/api-types";
6
+ import { useMacrostratColumnData, useMacrostratUnits } from "../data-provider";
7
+ import { ColumnSurfaces } from "./surfaces";
8
+ import { surfacesFromUnits } from "./types";
9
+
10
+ /** @deprecated Use `ColumnSurfaces`, which also labels the surfaces and
11
+ * styles them by boundary status. */
12
+ export function BoundaryAgeModelOverlay() {
13
+ return h(ColumnSurfaces, {
14
+ showLabels: false,
15
+ extent: "column",
16
+ fallbackToUnits: false,
17
+ });
18
+ }
19
+
20
+ /** @deprecated Use `ColumnSurfaces` with `surfaces: surfacesFromUnits(units)`,
21
+ * or rely on its default fallback to unit-derived surfaces. */
22
+ export function ComputedSurfacesOverlay() {
23
+ const units = useMacrostratUnits() as UnitLong[];
24
+ const { axisType } = useMacrostratColumnData();
25
+ const surfaces = useMemo(
26
+ () => surfacesFromUnits(units, { axisType }),
27
+ [units, axisType],
28
+ );
29
+ return h(ColumnSurfaces, { surfaces, showLabels: false, extent: "column" });
30
+ }
@@ -0,0 +1,168 @@
1
+ // Surface styling is keyed on boundary status (how the age is constrained)
2
+ // and boundary type (the nature of the contact). The tokens default to the
3
+ // design-system palette and can be overridden per column.
4
+ .column-surface-lines, .column-surface-labels, .surface-status-legend, .surface-tag
5
+ --surface-color-absolute: var(--pz-intent-danger, #c23030)
6
+ --surface-color-relative: var(--pz-intent-primary, #2d72d2)
7
+ --surface-color-spike: var(--pz-purple-500, #7961db)
8
+ --surface-color-imposed: var(--pz-orange-500, #c87619)
9
+ --surface-color-modeled: var(--pz-neutral-500, #8f99a8)
10
+ --surface-color-unspecified: var(--pz-neutral-400, #abb3bf)
11
+ --surface-color-derived: var(--pz-intent-success, #238551)
12
+ // The selection color is the column's, so a selected surface and a selected
13
+ // unit read as the same act
14
+ --surface-selected-color: var(--column-selection-color, var(--pz-accent-color, #7961db))
15
+ // Surfaces are drawn at one weight — the status shows in the dash pattern
16
+ // and the color, not the thickness — and the label's leader line matches it,
17
+ // so the stroke reads as continuous from the column into the tag.
18
+ --surface-line-width: 3px
19
+ --surface-leader-width: var(--surface-line-width)
20
+ // The outline of the box around a selected surface line
21
+ --surface-selection-width: 2px
22
+ // The tags keep the standard inset: it is what centers a note on the line
23
+ // that points at it, and it puts the tag's left edge exactly where the
24
+ // leader line ends, which the leader's overhang then tucks under
25
+
26
+ // Map statuses to a single color token that the lines, markers and tags share
27
+ .status-absolute
28
+ --surface-color: var(--surface-color-absolute)
29
+ .status-relative
30
+ --surface-color: var(--surface-color-relative)
31
+ .status-spike
32
+ --surface-color: var(--surface-color-spike)
33
+ .status-imposed
34
+ --surface-color: var(--surface-color-imposed)
35
+ .status-modeled
36
+ --surface-color: var(--surface-color-modeled)
37
+ .status-unspecified
38
+ --surface-color: var(--surface-color-unspecified)
39
+ .status-derived
40
+ --surface-color: var(--surface-color-derived)
41
+
42
+ // The dash pattern follows how the age is constrained: dated and
43
+ // interval-relative surfaces are solid, interpolated and unspecified ones
44
+ // dotted, imposed ones dashed. Weight is constant.
45
+ .status-absolute, .status-relative, .status-spike
46
+ --surface-line-style: solid
47
+ .status-imposed
48
+ --surface-line-style: dashed
49
+ .status-modeled, .status-unspecified, .status-derived
50
+ --surface-line-style: dotted
51
+
52
+ // Unconformable contacts are drawn as a double rule
53
+ .type-unconformity, .type-disconformity, .type-non-conformity, .type-angular-unconformity
54
+ --surface-line-double: 1
55
+ .type-fault
56
+ --surface-line-style: dashed
57
+
58
+ .column-surface-lines
59
+ position: absolute
60
+ top: 0
61
+ left: 0
62
+ width: 100%
63
+ pointer-events: none
64
+ z-index: 5
65
+ &.measuring
66
+ visibility: hidden
67
+
68
+ .surface-line
69
+ position: absolute
70
+ left: 0
71
+ right: 0
72
+ height: 0
73
+ // A comfortable hit area around a hairline
74
+ padding: 4px 0
75
+ transform: translateY(-4px)
76
+ pointer-events: none
77
+ color: var(--surface-color)
78
+ &.interactive
79
+ pointer-events: auto
80
+ cursor: pointer
81
+
82
+ .surface-line-inner
83
+ border-top: var(--surface-line-width, 1px) var(--surface-line-style, solid) var(--surface-color)
84
+ transform: translateY(calc(var(--surface-line-width, 1px) / -2))
85
+
86
+ &.interactive:hover .surface-line-inner
87
+ border-top-width: calc(var(--surface-line-width, 1px) + 1px)
88
+ filter: brightness(0.8)
89
+
90
+ // Selection outlines the box around the line; the line inside keeps the
91
+ // color of the interval it marks
92
+ &.selected
93
+ outline: var(--surface-selection-width) solid var(--surface-selected-color)
94
+ background-color: var(--pz-selected-background-color, transparent)
95
+
96
+ // Double rule for unconformable contacts
97
+ .surface-line
98
+ &.type-unconformity, &.type-disconformity, &.type-non-conformity, &.type-angular-unconformity
99
+ .surface-line-inner
100
+ border-bottom: var(--surface-line-width, 1px) var(--surface-line-style, solid) var(--surface-color)
101
+ height: 3px
102
+
103
+ .column-surface-labels
104
+ position: relative
105
+ :global(.col-note-label)
106
+ // We render our own label body
107
+ display: none
108
+ :global(.note-inner)
109
+ margin: 2px 5px
110
+ // The leader continues past both of its ends (`connectorOverhang`), so it
111
+ // has to paint outside the notes SVG: left over the gap to the column, right
112
+ // under the tag body
113
+ svg
114
+ overflow: visible
115
+ // It carries the surface's color (set per note as `--note-color`) and the
116
+ // weight of the line it runs into
117
+ :global(.link)
118
+ stroke-width: var(--surface-leader-width)
119
+
120
+ .surface-label
121
+ display: flex
122
+ flex-direction: row
123
+ align-items: center
124
+ gap: 6px
125
+ padding: 2px 4px
126
+ border-radius: 3px
127
+ cursor: pointer
128
+ line-height: 1.2
129
+ color: var(--column-text-color)
130
+ &:hover
131
+ background-color: var(--pz-hover-background-color, rgba(128, 128, 128, 0.12))
132
+ // Selection reads in the text itself rather than as a card around it
133
+ &.selected .surface-label-primary
134
+ font-weight: 700
135
+
136
+ .surface-label-marker
137
+ flex: none
138
+ width: 14px
139
+ height: 0
140
+ border-top: var(--surface-line-width, 1px) var(--surface-line-style, solid) var(--surface-color)
141
+ .type-unconformity &, .type-disconformity &, .type-non-conformity &, .type-angular-unconformity &
142
+ border-bottom: var(--surface-line-width, 1px) var(--surface-line-style, solid) var(--surface-color)
143
+ height: 3px
144
+
145
+ .surface-label-text
146
+ min-width: 0
147
+ display: flex
148
+ flex-direction: row
149
+ align-items: baseline
150
+ gap: 6px
151
+ white-space: nowrap
152
+
153
+ .surface-label-primary
154
+ font-size: 0.75rem
155
+ font-weight: 500
156
+ white-space: nowrap
157
+ // The tag keeps its name and detail on one line
158
+ :global(.bp6-tag), .surface-interval-tag
159
+ white-space: nowrap
160
+ vertical-align: middle
161
+
162
+ .surface-label-status
163
+ font-style: italic
164
+
165
+ .surface-label-secondary
166
+ font-size: 0.7rem
167
+ white-space: nowrap
168
+ color: var(--text-subtle-color, var(--pz-text-subtle-color, #5f6b7c))