@echelon-foundry/design-system 0.3.0 → 0.4.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 (73) hide show
  1. package/README.md +29 -0
  2. package/contracts/diagram-presentation.json +109 -0
  3. package/contracts/workflow-capabilities.json +25 -0
  4. package/dist/all.css +175 -3
  5. package/dist/build-metrics.json +32 -4
  6. package/dist/components.css +173 -3
  7. package/dist/foundations.css +2 -0
  8. package/dist/marketing/forma-all.css +176 -4
  9. package/dist/marketing/forma-echelon-marketing.css +3 -1
  10. package/dist/marketing/forma-marketing-theme-echelon.css +1 -1
  11. package/dist/marketing/forma-marketing.css +3 -1
  12. package/dist/marketing/forma-marketing.manifest.json +9 -9
  13. package/dist/marketing/forma-marketing.sha256 +4 -4
  14. package/dist/patterns/bounded-overflow.html +9 -0
  15. package/dist/patterns/button.html +5 -0
  16. package/dist/patterns/container.html +6 -0
  17. package/dist/patterns/date-time-field.html +5 -0
  18. package/dist/patterns/grid.html +5 -0
  19. package/dist/patterns/image-choice.html +2 -2
  20. package/dist/patterns/key-value-list.html +5 -0
  21. package/dist/patterns/section.html +4 -0
  22. package/dist/patterns/skip-link.html +6 -0
  23. package/dist/patterns/split.html +12 -0
  24. package/dist/patterns/surface.html +4 -0
  25. package/dist/patterns/text-field.html +5 -0
  26. package/dist/patterns/text-roles.html +7 -0
  27. package/dist/patterns/textarea.html +5 -0
  28. package/dist/patterns/visually-hidden.html +4 -0
  29. package/docs/MARKETING-SITES.md +1 -1
  30. package/docs/workflow/EMBEDDING.md +148 -0
  31. package/docs/workflow/EXTERNAL-PRODUCERS.md +34 -0
  32. package/docs/workflow/FORMAT.md +196 -0
  33. package/docs/workflow/RENDERING.md +90 -0
  34. package/docs/workflow/REQUIREMENTS-AUDIT-2026-10-01.md +55 -0
  35. package/docs/workflow/VALIDATION.md +91 -0
  36. package/examples/workflows/fixtures.json +101 -0
  37. package/examples/workflows/forma/workflows/accessible.forma-workflow.json +90 -0
  38. package/examples/workflows/forma/workflows/branching.forma-workflow.json +91 -0
  39. package/examples/workflows/forma/workflows/branded.forma-workflow.json +85 -0
  40. package/examples/workflows/forma/workflows/color-mapping.forma-workflow.json +111 -0
  41. package/examples/workflows/forma/workflows/colored.forma-workflow.json +131 -0
  42. package/examples/workflows/forma/workflows/edge-metadata.forma-workflow.json +66 -0
  43. package/examples/workflows/forma/workflows/embedded-edit.forma-workflow.json +117 -0
  44. package/examples/workflows/forma/workflows/embedded-view.forma-workflow.json +93 -0
  45. package/examples/workflows/forma/workflows/extensions.forma-workflow.json +90 -0
  46. package/examples/workflows/forma/workflows/groups.forma-workflow.json +89 -0
  47. package/examples/workflows/forma/workflows/interactive-export.forma-workflow.json +62 -0
  48. package/examples/workflows/forma/workflows/minimal.forma-workflow.json +17 -0
  49. package/examples/workflows/forma/workflows/mobile.forma-workflow.json +80 -0
  50. package/examples/workflows/forma/workflows/multi-step.forma-workflow.json +82 -0
  51. package/examples/workflows/forma/workflows/no-layout.forma-workflow.json +90 -0
  52. package/examples/workflows/forma/workflows/node-metadata.forma-workflow.json +80 -0
  53. package/examples/workflows/forma/workflows/runtime-ports.forma-workflow.json +118 -0
  54. package/examples/workflows/forma/workflows/static-export.forma-workflow.json +77 -0
  55. package/examples/workflows/forma/workflows/swimlanes.forma-workflow.json +130 -0
  56. package/figma/component-contracts.json +140 -0
  57. package/package.json +28 -9
  58. package/patterns/bounded-overflow.html +9 -0
  59. package/patterns/button.html +5 -0
  60. package/patterns/container.html +6 -0
  61. package/patterns/date-time-field.html +5 -0
  62. package/patterns/grid.html +5 -0
  63. package/patterns/image-choice.html +2 -2
  64. package/patterns/key-value-list.html +5 -0
  65. package/patterns/section.html +4 -0
  66. package/patterns/skip-link.html +6 -0
  67. package/patterns/split.html +12 -0
  68. package/patterns/surface.html +4 -0
  69. package/patterns/text-field.html +5 -0
  70. package/patterns/text-roles.html +7 -0
  71. package/patterns/textarea.html +5 -0
  72. package/patterns/visually-hidden.html +4 -0
  73. package/schemas/workflow/1.0/forma-workflow.schema.json +1045 -0
@@ -35,6 +35,8 @@
35
35
  font-size: 0.8125rem;
36
36
  line-height: 1.4;
37
37
  }
38
+ /* On a secondary surface, text-secondary falls below 4.5:1; use the surface's own text colour (forma#93). */
39
+ .ef-surface .ef-field__description { color: var(--ef-color-text-on-secondary-surface, #3a403c); }
38
40
 
39
41
  /* Motion model vocabulary --------------------------------------------- */
40
42
  /*
@@ -4340,6 +4342,8 @@
4340
4342
  * node: --ef-diagram-fill, --ef-diagram-stroke, --ef-diagram-accent,
4341
4343
  * --ef-diagram-foreground
4342
4344
  * connector: --ef-diagram-connector-stroke, --ef-diagram-connector-width
4345
+ * group: the node properties plus --ef-diagram-h (M2, forma#88)
4346
+ * The machine-readable contract is contracts/diagram-presentation.json.
4343
4347
  * Non-color cues: the visible node kind text, data-ef-shape, data-ef-line and
4344
4348
  * the textual relationship list. Focus never reads authored color.
4345
4349
  */
@@ -4384,6 +4388,46 @@
4384
4388
  }
4385
4389
  .ef-diagram-node[data-ef-shape="rounded"] { border-radius: var(--ef-primitive-spacing-3, 0.75rem); }
4386
4390
  .ef-diagram-node[data-ef-shape="pill"] { border-radius: 999px; padding-inline: var(--ef-primitive-spacing-5, 1.5rem); }
4391
+ /*
4392
+ * FMD-M2 (forma#88). Ellipse and diamond keep a real border (never a
4393
+ * background-only shape) so the boundary survives backgrounds-off print and
4394
+ * forced colors. --ef-diagram-h is an optional minimum height.
4395
+ */
4396
+ .ef-diagram-node { min-block-size: var(--ef-diagram-h, auto); }
4397
+ .ef-diagram-node[data-ef-shape="ellipse"] {
4398
+ border-radius: 50%;
4399
+ border-inline-start-width: 2px;
4400
+ border-block-end: 6px solid var(--ef-diagram-node-accent);
4401
+ padding: var(--ef-primitive-spacing-4, 1rem) calc(var(--ef-diagram-w, 12rem) * 0.14);
4402
+ place-content: center;
4403
+ text-align: center;
4404
+ }
4405
+ .ef-diagram-node[data-ef-shape="ellipse"] .ef-diagram-node__meta,
4406
+ .ef-diagram-node[data-ef-shape="diamond"] .ef-diagram-node__meta { grid-template-columns: auto auto; justify-content: center; text-align: start; }
4407
+ /*
4408
+ * A square box whose ::before is the diamond; the content sits in the
4409
+ * inscribed square. Padding is derived from --ef-diagram-w because percentage
4410
+ * padding would resolve against the canvas, not the node.
4411
+ */
4412
+ .ef-diagram-node[data-ef-shape="diamond"] {
4413
+ aspect-ratio: 1;
4414
+ padding: calc(var(--ef-diagram-w, 12rem) * 0.2);
4415
+ border: 0;
4416
+ background: transparent;
4417
+ place-content: center;
4418
+ text-align: center;
4419
+ isolation: isolate;
4420
+ }
4421
+ .ef-diagram-node[data-ef-shape="diamond"]::before {
4422
+ content: "";
4423
+ position: absolute;
4424
+ inset: 14.645%;
4425
+ z-index: -1;
4426
+ transform: rotate(45deg);
4427
+ border: 2px solid var(--ef-diagram-node-stroke);
4428
+ border-left: 6px solid var(--ef-diagram-node-accent);
4429
+ background: var(--ef-diagram-node-fill);
4430
+ }
4387
4431
  .ef-diagram-node :where(h3, p, dl, dd) { margin: 0; color: inherit; max-inline-size: none; }
4388
4432
  .ef-diagram-node__label { font-size: 1rem; line-height: 1.25; }
4389
4433
  .ef-diagram-node a { color: inherit; }
@@ -4428,20 +4472,146 @@
4428
4472
  vertical-align: middle;
4429
4473
  }
4430
4474
 
4475
+ /*
4476
+ * Groups, lanes and phases (FMD-M2). A boundary with a visible text label,
4477
+ * painted beneath connectors and nodes (place it first in the canvas).
4478
+ * Membership is consumer data: overlap on the canvas never implies it, so the
4479
+ * consumer states membership in text (node metadata or the relationship list).
4480
+ * Kind is text in __kind; data-ef-group only selects the presentation.
4481
+ */
4482
+ .ef-diagram-group {
4483
+ --ef-diagram-group-fill: var(--ef-diagram-fill, transparent);
4484
+ --ef-diagram-group-stroke: var(--ef-diagram-stroke, var(--ef-color-border-functional, #3a403c));
4485
+ --ef-diagram-group-accent: var(--ef-diagram-accent, var(--ef-diagram-group-stroke));
4486
+ --ef-diagram-group-foreground: var(--ef-diagram-foreground, var(--ef-color-text-primary, #171a18));
4487
+ position: absolute;
4488
+ left: var(--ef-diagram-x, 0);
4489
+ top: var(--ef-diagram-y, 0);
4490
+ inline-size: var(--ef-diagram-w, 20rem);
4491
+ block-size: var(--ef-diagram-h, 10rem);
4492
+ box-sizing: border-box;
4493
+ margin: 0;
4494
+ border: 2px solid var(--ef-diagram-group-stroke);
4495
+ border-block-start: 6px solid var(--ef-diagram-group-accent);
4496
+ background: var(--ef-diagram-group-fill);
4497
+ color: var(--ef-diagram-group-foreground);
4498
+ }
4499
+ .ef-diagram-group[data-ef-line="dashed"] { border-style: dashed; border-block-start-style: solid; }
4500
+ .ef-diagram-group[data-ef-line="dotted"] { border-style: dotted; border-block-start-style: solid; }
4501
+ .ef-diagram-group[data-ef-group="lane"] { border-block-start-width: 2px; border-inline-start-width: 6px; border-inline-start-color: var(--ef-diagram-group-accent); border-inline-start-style: solid; }
4502
+ .ef-diagram-group__header {
4503
+ display: flex;
4504
+ flex-wrap: wrap;
4505
+ gap: 0 var(--ef-primitive-spacing-2, 0.5rem);
4506
+ align-items: baseline;
4507
+ margin: 0;
4508
+ padding: var(--ef-primitive-spacing-1, 0.25rem) var(--ef-primitive-spacing-2, 0.5rem);
4509
+ color: inherit;
4510
+ font-size: 0.875rem;
4511
+ overflow-wrap: anywhere;
4512
+ }
4513
+ .ef-diagram-group__kind { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
4514
+ .ef-diagram-group__label { font-weight: 700; }
4515
+
4516
+ /*
4517
+ * Metadata value state (FMD-SCHEMA-006/007). The state is text; the border
4518
+ * style is a secondary cue. Explicit values carry no tag.
4519
+ */
4520
+ .ef-diagram-value-state {
4521
+ display: inline-block;
4522
+ margin-inline-start: var(--ef-primitive-spacing-1, 0.25rem);
4523
+ padding: 0 var(--ef-primitive-spacing-1, 0.25rem);
4524
+ border: 1px dashed currentColor;
4525
+ font-size: 0.75rem;
4526
+ font-style: normal;
4527
+ font-weight: 400;
4528
+ line-height: 1.4;
4529
+ text-transform: none;
4530
+ letter-spacing: normal;
4531
+ vertical-align: baseline;
4532
+ }
4533
+ .ef-diagram-value-state[data-ef-value-state="unknown"],
4534
+ .ef-diagram-value-state[data-ef-value-state="unavailable"] { border-style: dotted; }
4535
+ .ef-diagram-value-state[data-ef-value-state="invalid"] { border-style: solid; border-width: 2px; font-weight: 700; }
4536
+ .ef-diagram-value-state[data-ef-value-state="source-bound"] { border-style: double; border-width: 3px; }
4537
+
4431
4538
  @media (forced-colors: active) {
4432
- .ef-diagram-node, .ef-diagram-legend__swatch { border-color: CanvasText; background: Canvas; color: CanvasText; }
4539
+ .ef-diagram-node, .ef-diagram-legend__swatch, .ef-diagram-group { border-color: CanvasText; background: Canvas; color: CanvasText; }
4540
+ .ef-diagram-node[data-ef-shape="diamond"] { background: transparent; }
4541
+ .ef-diagram-node[data-ef-shape="diamond"]::before { border-color: CanvasText; background: Canvas; }
4433
4542
  .ef-diagram-connector { stroke: CanvasText; }
4434
4543
  .ef-diagram-marker { fill: CanvasText; }
4435
4544
  .ef-diagram-connector__label { background: Canvas; color: CanvasText; }
4436
4545
  }
4437
4546
 
4438
- /* Backgrounds-off and grayscale print: boundaries, kinds and line styles remain. */
4547
+ /*
4548
+ * Print (color, grayscale and backgrounds-off). Authored strokes and accents
4549
+ * stay, since borders print even when backgrounds are disabled. Without an
4550
+ * authored stroke, boundaries print black. Kinds, labels, line styles and
4551
+ * the relationship list carry meaning when fills disappear.
4552
+ */
4439
4553
  @media print {
4440
4554
  .ef-diagram__viewport { overflow: visible; border-color: #000; }
4441
- .ef-diagram-node { break-inside: avoid; border-color: #000; }
4555
+ .ef-diagram-node { break-inside: avoid; --ef-diagram-node-stroke: var(--ef-diagram-stroke, #000); }
4556
+ .ef-diagram-group { --ef-diagram-group-stroke: var(--ef-diagram-stroke, #000); }
4442
4557
  .ef-diagram { break-inside: avoid; }
4443
4558
  }
4444
4559
 
4560
+ /*
4561
+ * Diagram contract 2.1.0 (workflow capability, forma#93; ADR-0006): ports,
4562
+ * node status, node description and the membership list. Presentation only.
4563
+ * Ports are connection-point markers placed by the consumer on a node side
4564
+ * (data-ef-side) at an offset along it (--ef-diagram-port-offset, a
4565
+ * percentage). Status is text in the node's metadata; data-ef-status only
4566
+ * selects a secondary, non-colour cue (outline, border style).
4567
+ */
4568
+ .ef-diagram-node__description { font-size: 0.875rem; line-height: 1.35; overflow-wrap: anywhere; }
4569
+ .ef-diagram-port {
4570
+ position: absolute;
4571
+ inline-size: 0.75rem;
4572
+ block-size: 0.75rem;
4573
+ box-sizing: border-box;
4574
+ border: 2px solid var(--ef-diagram-node-stroke, var(--ef-color-border-functional, #3a403c));
4575
+ border-radius: 50%;
4576
+ background: var(--ef-color-surface-primary, #f2efe7);
4577
+ transform: translate(-50%, -50%);
4578
+ }
4579
+ .ef-diagram-port[data-ef-side="top"] { left: var(--ef-diagram-port-offset, 50%); top: 0; }
4580
+ .ef-diagram-port[data-ef-side="bottom"] { left: var(--ef-diagram-port-offset, 50%); top: 100%; }
4581
+ .ef-diagram-port[data-ef-side="left"] { left: 0; top: var(--ef-diagram-port-offset, 50%); }
4582
+ .ef-diagram-port[data-ef-side="right"] { left: 100%; top: var(--ef-diagram-port-offset, 50%); }
4583
+ .ef-diagram-port[data-ef-direction="in"] { border-radius: 0; }
4584
+ .ef-diagram-port[data-ef-direction="inout"] { border-radius: 2px; transform: translate(-50%, -50%) rotate(45deg); }
4585
+ .ef-diagram-port__label {
4586
+ position: absolute;
4587
+ inset-block-start: 100%;
4588
+ inset-inline-start: 50%;
4589
+ transform: translateX(-50%);
4590
+ padding: 0 0.125rem;
4591
+ background: var(--ef-color-surface-primary, #f2efe7);
4592
+ color: var(--ef-color-text-primary, #171a18);
4593
+ font-size: 0.6875rem;
4594
+ line-height: 1.2;
4595
+ white-space: nowrap;
4596
+ }
4597
+ .ef-diagram-node[data-ef-status="active"] { box-shadow: 0 0 0 3px var(--ef-color-surface-primary, #f2efe7), 0 0 0 6px var(--ef-diagram-node-stroke); }
4598
+ .ef-diagram-node[data-ef-status="blocked"],
4599
+ .ef-diagram-node[data-ef-status="failed"] { border-style: double; border-width: 4px; border-inline-start-width: 6px; }
4600
+ .ef-diagram-node[data-ef-status="skipped"],
4601
+ .ef-diagram-node[data-ef-status="cancelled"] { border-style: dashed; border-inline-start-style: solid; }
4602
+ .ef-diagram-node[data-ef-status="pending"],
4603
+ .ef-diagram-node[data-ef-status="waiting"] { border-style: dotted; border-inline-start-style: solid; }
4604
+ .ef-diagram__membership { display: grid; grid-template-columns: minmax(0, max-content) minmax(0, 1fr); gap: var(--ef-primitive-spacing-1, 0.25rem) var(--ef-primitive-spacing-3, 0.75rem); margin: 0; }
4605
+ .ef-diagram__membership :where(dt) { font-weight: 700; overflow-wrap: anywhere; }
4606
+ .ef-diagram__membership :where(dd) { margin: 0; overflow-wrap: anywhere; }
4607
+ @media (max-width: 30rem) {
4608
+ .ef-diagram__membership { grid-template-columns: minmax(0, 1fr); }
4609
+ }
4610
+ @media (forced-colors: active) {
4611
+ .ef-diagram-port, .ef-diagram-port__label { border-color: CanvasText; background: Canvas; color: CanvasText; }
4612
+ .ef-diagram-node[data-ef-status="active"] { box-shadow: none; outline: 3px solid CanvasText; outline-offset: 3px; }
4613
+ }
4614
+
4445
4615
  /* BEGIN generated: character-grid coordinates (tools/character-grid-css.mjs) */
4446
4616
  .ef-character-grid[data-ef-rows="1"] { --ef-grid-rows: 1; }
4447
4617
  .ef-character-grid[data-ef-rows="2"] { --ef-grid-rows: 2; }
@@ -107,6 +107,8 @@
107
107
 
108
108
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
109
109
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
110
+ /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
111
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
110
112
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
111
113
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
112
114
  .ef-surface { min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
@@ -1,4 +1,4 @@
1
- /*! Forma 0.3.0 | forma-all.css | bundle echelon-marketing
1
+ /*! Forma 0.4.0 | forma-all.css | bundle echelon-marketing
2
2
  Complete Forma surface (application components, assessment, and marketing layers) for sites that also present Forma application patterns such as data grids or status lozenges.
3
3
  Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
4
4
 
@@ -647,6 +647,8 @@
647
647
 
648
648
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
649
649
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
650
+ /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
651
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
650
652
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
651
653
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
652
654
  .ef-surface { min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
@@ -779,6 +781,8 @@
779
781
  font-size: 0.8125rem;
780
782
  line-height: 1.4;
781
783
  }
784
+ /* On a secondary surface, text-secondary falls below 4.5:1; use the surface's own text colour (forma#93). */
785
+ .ef-surface .ef-field__description { color: var(--ef-color-text-on-secondary-surface, #3a403c); }
782
786
 
783
787
  /* Motion model vocabulary --------------------------------------------- */
784
788
  /*
@@ -5084,6 +5088,8 @@
5084
5088
  * node: --ef-diagram-fill, --ef-diagram-stroke, --ef-diagram-accent,
5085
5089
  * --ef-diagram-foreground
5086
5090
  * connector: --ef-diagram-connector-stroke, --ef-diagram-connector-width
5091
+ * group: the node properties plus --ef-diagram-h (M2, forma#88)
5092
+ * The machine-readable contract is contracts/diagram-presentation.json.
5087
5093
  * Non-color cues: the visible node kind text, data-ef-shape, data-ef-line and
5088
5094
  * the textual relationship list. Focus never reads authored color.
5089
5095
  */
@@ -5128,6 +5134,46 @@
5128
5134
  }
5129
5135
  .ef-diagram-node[data-ef-shape="rounded"] { border-radius: var(--ef-primitive-spacing-3, 0.75rem); }
5130
5136
  .ef-diagram-node[data-ef-shape="pill"] { border-radius: 999px; padding-inline: var(--ef-primitive-spacing-5, 1.5rem); }
5137
+ /*
5138
+ * FMD-M2 (forma#88). Ellipse and diamond keep a real border (never a
5139
+ * background-only shape) so the boundary survives backgrounds-off print and
5140
+ * forced colors. --ef-diagram-h is an optional minimum height.
5141
+ */
5142
+ .ef-diagram-node { min-block-size: var(--ef-diagram-h, auto); }
5143
+ .ef-diagram-node[data-ef-shape="ellipse"] {
5144
+ border-radius: 50%;
5145
+ border-inline-start-width: 2px;
5146
+ border-block-end: 6px solid var(--ef-diagram-node-accent);
5147
+ padding: var(--ef-primitive-spacing-4, 1rem) calc(var(--ef-diagram-w, 12rem) * 0.14);
5148
+ place-content: center;
5149
+ text-align: center;
5150
+ }
5151
+ .ef-diagram-node[data-ef-shape="ellipse"] .ef-diagram-node__meta,
5152
+ .ef-diagram-node[data-ef-shape="diamond"] .ef-diagram-node__meta { grid-template-columns: auto auto; justify-content: center; text-align: start; }
5153
+ /*
5154
+ * A square box whose ::before is the diamond; the content sits in the
5155
+ * inscribed square. Padding is derived from --ef-diagram-w because percentage
5156
+ * padding would resolve against the canvas, not the node.
5157
+ */
5158
+ .ef-diagram-node[data-ef-shape="diamond"] {
5159
+ aspect-ratio: 1;
5160
+ padding: calc(var(--ef-diagram-w, 12rem) * 0.2);
5161
+ border: 0;
5162
+ background: transparent;
5163
+ place-content: center;
5164
+ text-align: center;
5165
+ isolation: isolate;
5166
+ }
5167
+ .ef-diagram-node[data-ef-shape="diamond"]::before {
5168
+ content: "";
5169
+ position: absolute;
5170
+ inset: 14.645%;
5171
+ z-index: -1;
5172
+ transform: rotate(45deg);
5173
+ border: 2px solid var(--ef-diagram-node-stroke);
5174
+ border-left: 6px solid var(--ef-diagram-node-accent);
5175
+ background: var(--ef-diagram-node-fill);
5176
+ }
5131
5177
  .ef-diagram-node :where(h3, p, dl, dd) { margin: 0; color: inherit; max-inline-size: none; }
5132
5178
  .ef-diagram-node__label { font-size: 1rem; line-height: 1.25; }
5133
5179
  .ef-diagram-node a { color: inherit; }
@@ -5172,20 +5218,146 @@
5172
5218
  vertical-align: middle;
5173
5219
  }
5174
5220
 
5221
+ /*
5222
+ * Groups, lanes and phases (FMD-M2). A boundary with a visible text label,
5223
+ * painted beneath connectors and nodes (place it first in the canvas).
5224
+ * Membership is consumer data: overlap on the canvas never implies it, so the
5225
+ * consumer states membership in text (node metadata or the relationship list).
5226
+ * Kind is text in __kind; data-ef-group only selects the presentation.
5227
+ */
5228
+ .ef-diagram-group {
5229
+ --ef-diagram-group-fill: var(--ef-diagram-fill, transparent);
5230
+ --ef-diagram-group-stroke: var(--ef-diagram-stroke, var(--ef-color-border-functional, #3a403c));
5231
+ --ef-diagram-group-accent: var(--ef-diagram-accent, var(--ef-diagram-group-stroke));
5232
+ --ef-diagram-group-foreground: var(--ef-diagram-foreground, var(--ef-color-text-primary, #171a18));
5233
+ position: absolute;
5234
+ left: var(--ef-diagram-x, 0);
5235
+ top: var(--ef-diagram-y, 0);
5236
+ inline-size: var(--ef-diagram-w, 20rem);
5237
+ block-size: var(--ef-diagram-h, 10rem);
5238
+ box-sizing: border-box;
5239
+ margin: 0;
5240
+ border: 2px solid var(--ef-diagram-group-stroke);
5241
+ border-block-start: 6px solid var(--ef-diagram-group-accent);
5242
+ background: var(--ef-diagram-group-fill);
5243
+ color: var(--ef-diagram-group-foreground);
5244
+ }
5245
+ .ef-diagram-group[data-ef-line="dashed"] { border-style: dashed; border-block-start-style: solid; }
5246
+ .ef-diagram-group[data-ef-line="dotted"] { border-style: dotted; border-block-start-style: solid; }
5247
+ .ef-diagram-group[data-ef-group="lane"] { border-block-start-width: 2px; border-inline-start-width: 6px; border-inline-start-color: var(--ef-diagram-group-accent); border-inline-start-style: solid; }
5248
+ .ef-diagram-group__header {
5249
+ display: flex;
5250
+ flex-wrap: wrap;
5251
+ gap: 0 var(--ef-primitive-spacing-2, 0.5rem);
5252
+ align-items: baseline;
5253
+ margin: 0;
5254
+ padding: var(--ef-primitive-spacing-1, 0.25rem) var(--ef-primitive-spacing-2, 0.5rem);
5255
+ color: inherit;
5256
+ font-size: 0.875rem;
5257
+ overflow-wrap: anywhere;
5258
+ }
5259
+ .ef-diagram-group__kind { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
5260
+ .ef-diagram-group__label { font-weight: 700; }
5261
+
5262
+ /*
5263
+ * Metadata value state (FMD-SCHEMA-006/007). The state is text; the border
5264
+ * style is a secondary cue. Explicit values carry no tag.
5265
+ */
5266
+ .ef-diagram-value-state {
5267
+ display: inline-block;
5268
+ margin-inline-start: var(--ef-primitive-spacing-1, 0.25rem);
5269
+ padding: 0 var(--ef-primitive-spacing-1, 0.25rem);
5270
+ border: 1px dashed currentColor;
5271
+ font-size: 0.75rem;
5272
+ font-style: normal;
5273
+ font-weight: 400;
5274
+ line-height: 1.4;
5275
+ text-transform: none;
5276
+ letter-spacing: normal;
5277
+ vertical-align: baseline;
5278
+ }
5279
+ .ef-diagram-value-state[data-ef-value-state="unknown"],
5280
+ .ef-diagram-value-state[data-ef-value-state="unavailable"] { border-style: dotted; }
5281
+ .ef-diagram-value-state[data-ef-value-state="invalid"] { border-style: solid; border-width: 2px; font-weight: 700; }
5282
+ .ef-diagram-value-state[data-ef-value-state="source-bound"] { border-style: double; border-width: 3px; }
5283
+
5175
5284
  @media (forced-colors: active) {
5176
- .ef-diagram-node, .ef-diagram-legend__swatch { border-color: CanvasText; background: Canvas; color: CanvasText; }
5285
+ .ef-diagram-node, .ef-diagram-legend__swatch, .ef-diagram-group { border-color: CanvasText; background: Canvas; color: CanvasText; }
5286
+ .ef-diagram-node[data-ef-shape="diamond"] { background: transparent; }
5287
+ .ef-diagram-node[data-ef-shape="diamond"]::before { border-color: CanvasText; background: Canvas; }
5177
5288
  .ef-diagram-connector { stroke: CanvasText; }
5178
5289
  .ef-diagram-marker { fill: CanvasText; }
5179
5290
  .ef-diagram-connector__label { background: Canvas; color: CanvasText; }
5180
5291
  }
5181
5292
 
5182
- /* Backgrounds-off and grayscale print: boundaries, kinds and line styles remain. */
5293
+ /*
5294
+ * Print (color, grayscale and backgrounds-off). Authored strokes and accents
5295
+ * stay, since borders print even when backgrounds are disabled. Without an
5296
+ * authored stroke, boundaries print black. Kinds, labels, line styles and
5297
+ * the relationship list carry meaning when fills disappear.
5298
+ */
5183
5299
  @media print {
5184
5300
  .ef-diagram__viewport { overflow: visible; border-color: #000; }
5185
- .ef-diagram-node { break-inside: avoid; border-color: #000; }
5301
+ .ef-diagram-node { break-inside: avoid; --ef-diagram-node-stroke: var(--ef-diagram-stroke, #000); }
5302
+ .ef-diagram-group { --ef-diagram-group-stroke: var(--ef-diagram-stroke, #000); }
5186
5303
  .ef-diagram { break-inside: avoid; }
5187
5304
  }
5188
5305
 
5306
+ /*
5307
+ * Diagram contract 2.1.0 (workflow capability, forma#93; ADR-0006): ports,
5308
+ * node status, node description and the membership list. Presentation only.
5309
+ * Ports are connection-point markers placed by the consumer on a node side
5310
+ * (data-ef-side) at an offset along it (--ef-diagram-port-offset, a
5311
+ * percentage). Status is text in the node's metadata; data-ef-status only
5312
+ * selects a secondary, non-colour cue (outline, border style).
5313
+ */
5314
+ .ef-diagram-node__description { font-size: 0.875rem; line-height: 1.35; overflow-wrap: anywhere; }
5315
+ .ef-diagram-port {
5316
+ position: absolute;
5317
+ inline-size: 0.75rem;
5318
+ block-size: 0.75rem;
5319
+ box-sizing: border-box;
5320
+ border: 2px solid var(--ef-diagram-node-stroke, var(--ef-color-border-functional, #3a403c));
5321
+ border-radius: 50%;
5322
+ background: var(--ef-color-surface-primary, #f2efe7);
5323
+ transform: translate(-50%, -50%);
5324
+ }
5325
+ .ef-diagram-port[data-ef-side="top"] { left: var(--ef-diagram-port-offset, 50%); top: 0; }
5326
+ .ef-diagram-port[data-ef-side="bottom"] { left: var(--ef-diagram-port-offset, 50%); top: 100%; }
5327
+ .ef-diagram-port[data-ef-side="left"] { left: 0; top: var(--ef-diagram-port-offset, 50%); }
5328
+ .ef-diagram-port[data-ef-side="right"] { left: 100%; top: var(--ef-diagram-port-offset, 50%); }
5329
+ .ef-diagram-port[data-ef-direction="in"] { border-radius: 0; }
5330
+ .ef-diagram-port[data-ef-direction="inout"] { border-radius: 2px; transform: translate(-50%, -50%) rotate(45deg); }
5331
+ .ef-diagram-port__label {
5332
+ position: absolute;
5333
+ inset-block-start: 100%;
5334
+ inset-inline-start: 50%;
5335
+ transform: translateX(-50%);
5336
+ padding: 0 0.125rem;
5337
+ background: var(--ef-color-surface-primary, #f2efe7);
5338
+ color: var(--ef-color-text-primary, #171a18);
5339
+ font-size: 0.6875rem;
5340
+ line-height: 1.2;
5341
+ white-space: nowrap;
5342
+ }
5343
+ .ef-diagram-node[data-ef-status="active"] { box-shadow: 0 0 0 3px var(--ef-color-surface-primary, #f2efe7), 0 0 0 6px var(--ef-diagram-node-stroke); }
5344
+ .ef-diagram-node[data-ef-status="blocked"],
5345
+ .ef-diagram-node[data-ef-status="failed"] { border-style: double; border-width: 4px; border-inline-start-width: 6px; }
5346
+ .ef-diagram-node[data-ef-status="skipped"],
5347
+ .ef-diagram-node[data-ef-status="cancelled"] { border-style: dashed; border-inline-start-style: solid; }
5348
+ .ef-diagram-node[data-ef-status="pending"],
5349
+ .ef-diagram-node[data-ef-status="waiting"] { border-style: dotted; border-inline-start-style: solid; }
5350
+ .ef-diagram__membership { display: grid; grid-template-columns: minmax(0, max-content) minmax(0, 1fr); gap: var(--ef-primitive-spacing-1, 0.25rem) var(--ef-primitive-spacing-3, 0.75rem); margin: 0; }
5351
+ .ef-diagram__membership :where(dt) { font-weight: 700; overflow-wrap: anywhere; }
5352
+ .ef-diagram__membership :where(dd) { margin: 0; overflow-wrap: anywhere; }
5353
+ @media (max-width: 30rem) {
5354
+ .ef-diagram__membership { grid-template-columns: minmax(0, 1fr); }
5355
+ }
5356
+ @media (forced-colors: active) {
5357
+ .ef-diagram-port, .ef-diagram-port__label { border-color: CanvasText; background: Canvas; color: CanvasText; }
5358
+ .ef-diagram-node[data-ef-status="active"] { box-shadow: none; outline: 3px solid CanvasText; outline-offset: 3px; }
5359
+ }
5360
+
5189
5361
  /* BEGIN generated: character-grid coordinates (tools/character-grid-css.mjs) */
5190
5362
  .ef-character-grid[data-ef-rows="1"] { --ef-grid-rows: 1; }
5191
5363
  .ef-character-grid[data-ef-rows="2"] { --ef-grid-rows: 2; }
@@ -1,4 +1,4 @@
1
- /*! Forma 0.3.0 | forma-echelon-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.4.0 | forma-echelon-marketing.css | bundle echelon-marketing
2
2
  Single-file Echelon marketing presentation: forma-marketing.css followed by the Echelon theme. Most sites load only this file.
3
3
  Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
4
4
 
@@ -747,6 +747,8 @@
747
747
 
748
748
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
749
749
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
750
+ /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
751
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
750
752
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
751
753
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
752
754
  .ef-surface { min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
@@ -1,4 +1,4 @@
1
- /*! Forma 0.3.0 | forma-marketing-theme-echelon.css | bundle echelon-marketing
1
+ /*! Forma 0.4.0 | forma-marketing-theme-echelon.css | bundle echelon-marketing
2
2
  Echelon Marketing Theme: compiled theme tokens and theme hooks. Load after forma-marketing.css.
3
3
  Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
4
4
 
@@ -1,4 +1,4 @@
1
- /*! Forma 0.3.0 | forma-marketing.css | bundle echelon-marketing
1
+ /*! Forma 0.4.0 | forma-marketing.css | bundle echelon-marketing
2
2
  Brand-neutral marketing layer: core tokens, role defaults, Forma foundations, marketing foundations, components, and layouts.
3
3
  Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
4
4
 
@@ -746,6 +746,8 @@
746
746
 
747
747
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
748
748
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
749
+ /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
750
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
749
751
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
750
752
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
751
753
  .ef-surface { min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "forma": "0.3.0",
3
+ "forma": "0.4.0",
4
4
  "bundle": "echelon-marketing",
5
5
  "description": "Forma presentation artifacts for Echelon marketing sites. Every artifact is a single self-contained CSS file with no imports or relative URLs.",
6
- "releaseTag": "v0.3.0",
6
+ "releaseTag": "v0.4.0",
7
7
  "artifacts": [
8
8
  {
9
9
  "file": "forma-marketing.css",
10
10
  "description": "Brand-neutral marketing layer: core tokens, role defaults, Forma foundations, marketing foundations, components, and layouts.",
11
- "bytes": 77198,
12
- "sha256": "9aec7b369ef6b60ceb5c8db51f088d232d2da07816cf9074ef1859fe035f4a65",
11
+ "bytes": 77452,
12
+ "sha256": "e9f2f952cbec98bbdf270d8a6ed639fa8abc57d45053a78acbb85775102f35ea",
13
13
  "sources": [
14
14
  "dist/tokens.css",
15
15
  "src/marketing/roles.css",
@@ -23,7 +23,7 @@
23
23
  "file": "forma-marketing-theme-echelon.css",
24
24
  "description": "Echelon Marketing Theme: compiled theme tokens and theme hooks. Load after forma-marketing.css.",
25
25
  "bytes": 6295,
26
- "sha256": "157f1ec870a8825a0bbbfed4e719db7b92962e2625a8165f73abc4214c01e907",
26
+ "sha256": "295da09137526ef046ec46101db12231755d13b5ce5377a180f34ea1064417c6",
27
27
  "sources": [
28
28
  "build/marketing/echelon-marketing.tokens.css",
29
29
  "themes/echelon/marketing.css"
@@ -32,8 +32,8 @@
32
32
  {
33
33
  "file": "forma-echelon-marketing.css",
34
34
  "description": "Single-file Echelon marketing presentation: forma-marketing.css followed by the Echelon theme. Most sites load only this file.",
35
- "bytes": 83338,
36
- "sha256": "383948a077c070ad4bda90688a84f65bd319fde1e326197d5917f0dde632f947",
35
+ "bytes": 83592,
36
+ "sha256": "c0634b649d91f3a4fda166c3b926655fd1dc0270406e0a2bb43dc38303fa7709",
37
37
  "sources": [
38
38
  "artifact:forma-marketing.css",
39
39
  "artifact:forma-marketing-theme-echelon.css"
@@ -42,8 +42,8 @@
42
42
  {
43
43
  "file": "forma-all.css",
44
44
  "description": "Complete Forma surface (application components, assessment, and marketing layers) for sites that also present Forma application patterns such as data grids or status lozenges.",
45
- "bytes": 301112,
46
- "sha256": "06a5a33e06a5aefd7dead874c259c67611907074ec95346d30e0cdb57d18ea37",
45
+ "bytes": 310120,
46
+ "sha256": "0d74d63c0cf65c732e32c1cdc66d241cec660b198b1151c54b2edafc5e32fa68",
47
47
  "sources": [
48
48
  "dist/all.css"
49
49
  ]
@@ -1,4 +1,4 @@
1
- 9aec7b369ef6b60ceb5c8db51f088d232d2da07816cf9074ef1859fe035f4a65 forma-marketing.css
2
- 157f1ec870a8825a0bbbfed4e719db7b92962e2625a8165f73abc4214c01e907 forma-marketing-theme-echelon.css
3
- 383948a077c070ad4bda90688a84f65bd319fde1e326197d5917f0dde632f947 forma-echelon-marketing.css
4
- 06a5a33e06a5aefd7dead874c259c67611907074ec95346d30e0cdb57d18ea37 forma-all.css
1
+ e9f2f952cbec98bbdf270d8a6ed639fa8abc57d45053a78acbb85775102f35ea forma-marketing.css
2
+ 295da09137526ef046ec46101db12231755d13b5ce5377a180f34ea1064417c6 forma-marketing-theme-echelon.css
3
+ c0634b649d91f3a4fda166c3b926655fd1dc0270406e0a2bb43dc38303fa7709 forma-echelon-marketing.css
4
+ 0d74d63c0cf65c732e32c1cdc66d241cec660b198b1151c54b2edafc5e32fa68 forma-all.css
@@ -0,0 +1,9 @@
1
+ <div class="ef-bounded-overflow" role="region" aria-label="Quarterly totals by region" tabindex="0">
2
+ <table>
3
+ <thead><tr><th scope="col">Region</th><th scope="col">Q1</th><th scope="col">Q2</th><th scope="col">Q3</th><th scope="col">Q4</th><th scope="col">Year</th></tr></thead>
4
+ <tbody>
5
+ <tr><th scope="row">North America</th><td>1,204,000</td><td>1,318,500</td><td>1,402,250</td><td>1,511,000</td><td>5,435,750</td></tr>
6
+ <tr><th scope="row">Europe</th><td>904,100</td><td>931,000</td><td>1,010,400</td><td>1,120,900</td><td>3,966,400</td></tr>
7
+ </tbody>
8
+ </table>
9
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="ef-actions">
2
+ <button type="submit">Save changes</button>
3
+ <button type="button">Cancel</button>
4
+ <a class="ef-button" href="#button-help">Read the help article</a>
5
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="ef-site">
2
+ <div class="ef-container" data-ef-width="narrow">
3
+ <h2>Privacy notice</h2>
4
+ <p>Narrow containers keep reading-focused regions at a comfortable measure while the page background stays full width.</p>
5
+ </div>
6
+ </div>
@@ -0,0 +1,5 @@
1
+ <div class="ef-field">
2
+ <label class="ef-field__label" for="date-time-field-due">Due date</label>
3
+ <span class="ef-field__description" id="date-time-field-due-description">The browser supplies its own date picker; typing the date also works.</span>
4
+ <input id="date-time-field-due" name="due" type="date" aria-describedby="date-time-field-due-description">
5
+ </div>
@@ -0,0 +1,5 @@
1
+ <ul class="ef-grid" aria-label="Environments" role="list">
2
+ <li class="ef-surface"><h3>Production</h3><p>12 services, all reporting.</p></li>
3
+ <li class="ef-surface"><h3>Staging</h3><p>12 services, 1 deploying.</p></li>
4
+ <li class="ef-surface"><h3>Development</h3><p>9 services, 3 idle.</p></li>
5
+ </ul>
@@ -4,7 +4,7 @@
4
4
  <label class="ef-choice ef-choice--media">
5
5
  <input type="radio" name="layout-choice" value="compact" required>
6
6
  <span class="ef-choice__media">
7
- <img src="/path/to/compact-layout.png" alt="">
7
+ <img width="160" height="90" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 90'%3E%3Crect width='160' height='90' fill='%23e3e0d7'/%3E%3Crect x='10' y='10' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='21' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='32' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='43' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='54' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='65' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='76' width='140' height='8' fill='%23686d68'/%3E%3C/svg%3E" alt="">
8
8
  </span>
9
9
  <span class="ef-choice__content">
10
10
  <strong>Compact layout</strong>
@@ -15,7 +15,7 @@
15
15
  <label class="ef-choice ef-choice--media">
16
16
  <input type="radio" name="layout-choice" value="spacious">
17
17
  <span class="ef-choice__media">
18
- <img src="/path/to/spacious-layout.png" alt="">
18
+ <img width="160" height="90" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 90'%3E%3Crect width='160' height='90' fill='%23e3e0d7'/%3E%3Crect x='10' y='10' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='34' width='140' height='8' fill='%23686d68'/%3E%3Crect x='10' y='58' width='140' height='8' fill='%23686d68'/%3E%3C/svg%3E" alt="">
19
19
  </span>
20
20
  <span class="ef-choice__content">
21
21
  <strong>Spacious layout</strong>
@@ -0,0 +1,5 @@
1
+ <dl class="ef-key-value-list">
2
+ <div><dt>Account</dt><dd>Northwind Logistics</dd></div>
3
+ <div><dt>Plan</dt><dd>Team, billed annually</dd></div>
4
+ <div><dt>Renewal</dt><dd><time datetime="2027-03-01">1 March 2027</time></dd></div>
5
+ </dl>
@@ -0,0 +1,4 @@
1
+ <section class="ef-section" aria-labelledby="section-title">
2
+ <h2 id="section-title">Recent activity</h2>
3
+ <p>Sections separate page regions with a consistent block rhythm.</p>
4
+ </section>
@@ -0,0 +1,6 @@
1
+ <div class="ef-site">
2
+ <a class="ef-skip-link" href="#skip-link-main">Skip to main content</a>
3
+ <main id="skip-link-main" tabindex="-1">
4
+ <h1>Account settings</h1>
5
+ </main>
6
+ </div>