-
+ .route-journey-controls button:disabled {
+ cursor: default;
+ opacity: 0.34;
+ }
+ .route-journey-play { color: var(--backend-stroke) !important; }
+ .route-journey-overview { color: var(--frontend-stroke) !important; }
+ .route-journey-controls[data-playing="true"] .route-journey-play {
+ border-color: color-mix(in srgb, var(--backend-stroke) 64%, var(--toolbar-border));
+ background: color-mix(in srgb, var(--backend-fill) 62%, transparent);
+ }
+ .route-probe[data-state="error"] {
+ border-color: color-mix(in srgb, var(--security-stroke) 72%, var(--toolbar-border));
+ }
+ .route-probe[data-state="error"] .route-probe-status { color: var(--security-stroke); }
+
+ .focus-chip {
+ position: absolute;
+ left: 1rem;
+ top: 1rem;
+ z-index: 12;
+ display: block;
+ width: min(22rem, calc(100% - 13rem));
+ max-width: calc(100% - 13rem);
+ overflow: hidden;
+ border: 1px solid var(--frontend-stroke);
+ border-radius: 0.85rem;
+ background: var(--toolbar-menu-bg);
+ color: var(--toolbar-text);
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.3);
+ backdrop-filter: blur(16px);
+ font-size: 0.6875rem;
+ }
+ .focus-chip[hidden] { display: none; }
+ html[data-preset="blueprint"] .focus-chip { border-radius: 0.3rem; }
+ .focus-chip button {
+ flex: none;
+ padding: 0.3rem 0.45rem;
+ border-radius: 0.35rem;
+ font-size: 0.625rem;
+ }
+ .relationship-lens-head {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 0.75rem;
+ padding: 0.7rem 0.75rem 0.65rem;
+ border-bottom: 1px solid var(--toolbar-border);
+ background: linear-gradient(120deg, color-mix(in srgb, var(--frontend-fill) 34%, transparent), transparent 72%);
+ }
+ .relationship-lens-copy { min-width: 0; }
+ .relationship-lens-actions {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 0.18rem;
+ }
+ .relationship-lens-actions button { white-space: nowrap; }
+ .relationship-lens-actions #btn-focus-clear {
+ display: grid;
+ width: 1.75rem;
+ height: 1.75rem;
+ padding: 0;
+ place-items: center;
+ color: var(--text-muted);
+ font-size: 1rem;
+ line-height: 1;
+ }
+ .relationship-lens-actions #btn-focus-clear:hover,
+ .relationship-lens-actions #btn-focus-clear:focus-visible {
+ background: var(--toolbar-hover);
+ color: var(--text);
+ }
+ .relationship-lens-actions #btn-focus-copy { color: var(--frontend-stroke); }
+ .relationship-lens-actions #btn-focus-relations { display: none; }
+ .relationship-lens-eyebrow {
+ display: block;
+ margin-bottom: 0.18rem;
+ color: var(--frontend-stroke);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ }
+ .relationship-lens-title {
+ display: block;
+ overflow: hidden;
+ color: var(--text);
+ font-size: 0.8rem;
+ line-height: 1.35;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .semantic-passport-detail {
+ display: block;
+ margin-top: 0.12rem;
+ overflow: hidden;
+ color: var(--text-muted);
+ font-size: 0.625rem;
+ line-height: 1.4;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .semantic-passport-detail[hidden] { display: none; }
+ .semantic-passport-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.24rem;
+ margin-top: 0.42rem;
+ }
+ .semantic-passport-meta span,
+ .semantic-passport-meta code {
+ max-width: 100%;
+ overflow: hidden;
+ padding: 0.14rem 0.34rem;
+ border: 1px solid color-mix(in srgb, var(--toolbar-border) 82%, transparent);
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--panel) 68%, transparent);
+ color: var(--text-faint);
+ font: inherit;
+ font-size: 0.5rem;
+ letter-spacing: 0.04em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .semantic-passport-meta [data-passport="kind"] {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 50%, var(--toolbar-border));
+ color: var(--frontend-stroke);
+ font-weight: 800;
+ text-transform: uppercase;
+ }
+ .semantic-passport-meta [data-passport="tag"] { color: var(--cloud-stroke); }
+ .semantic-passport-meta [hidden] { display: none; }
+ html[data-preset="blueprint"] .semantic-passport-meta span,
+ html[data-preset="blueprint"] .semantic-passport-meta code { border-radius: 0.1rem; }
+ .semantic-passport-evidence {
+ margin-top: 0.52rem;
+ padding: 0.5rem;
+ border: 1px solid color-mix(in srgb, var(--backend-stroke) 32%, var(--toolbar-border));
+ border-radius: 0.55rem;
+ background: color-mix(in srgb, var(--backend-fill) 18%, var(--panel));
+ }
+ .semantic-passport-evidence[hidden] { display: none; }
+ html[data-preset="blueprint"] .semantic-passport-evidence { border-radius: 0.18rem; }
+ .semantic-passport-evidence-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.55rem;
+ margin-bottom: 0.35rem;
+ }
+ .semantic-passport-evidence-status {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.28rem;
+ color: var(--backend-stroke);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ }
+ .semantic-passport-evidence-status::before {
+ content: '';
+ width: 0.38rem;
+ height: 0.38rem;
+ border-radius: 50%;
+ background: var(--backend-stroke);
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--backend-stroke) 14%, transparent);
+ }
+ .semantic-passport-repository {
+ min-width: 0;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .semantic-passport-repository:hover,
+ .semantic-passport-repository:focus-visible { color: var(--backend-stroke); }
+ .semantic-passport-evidence-links { display: grid; gap: 0.22rem; }
+ .semantic-passport-source {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 0.15rem 0.45rem;
+ align-items: center;
+ padding: 0.34rem 0.4rem;
+ border: 1px solid transparent;
+ border-radius: 0.38rem;
+ color: var(--text-muted);
+ text-decoration: none;
+ }
+ .semantic-passport-source:hover,
+ .semantic-passport-source:focus-visible {
+ border-color: color-mix(in srgb, var(--backend-stroke) 42%, transparent);
+ background: color-mix(in srgb, var(--backend-fill) 32%, transparent);
+ color: var(--text);
+ }
+ .semantic-passport-source strong {
+ overflow: hidden;
+ font-size: 0.5625rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .semantic-passport-source code {
+ color: var(--backend-stroke);
+ font: inherit;
+ font-size: 0.5rem;
+ }
+ .semantic-passport-source small {
+ grid-column: 1 / -1;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .relationship-lens-summary {
+ display: block;
+ margin-top: 0.12rem;
+ color: var(--text-faint);
+ font-size: 0.5625rem;
+ }
+ .semantic-passport-reach {
+ display: grid;
+ gap: 0.28rem;
+ margin-top: 0.52rem;
+ padding-top: 0.48rem;
+ border-top: 1px solid color-mix(in srgb, var(--toolbar-border) 76%, transparent);
+ }
+ .semantic-passport-reach[hidden] { display: none; }
+ .semantic-passport-reach-label {
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ }
+ .semantic-passport-reach-actions {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 0.25rem;
+ }
+ .semantic-passport-reach-actions button {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.5rem;
+ min-width: 0;
+ min-height: 2rem;
+ padding: 0.32rem 0.42rem;
+ border: 1px solid color-mix(in srgb, var(--toolbar-border) 86%, transparent);
+ background: color-mix(in srgb, var(--panel) 70%, transparent);
+ color: var(--text-muted);
+ text-align: left;
+ }
+ .semantic-passport-reach-actions button:hover:not(:disabled),
+ .semantic-passport-reach-actions button:focus-visible {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 62%, var(--toolbar-border));
+ color: var(--text);
+ }
+ .semantic-passport-reach-actions button:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: 1px;
+ }
+ .semantic-passport-reach-actions button:disabled {
+ cursor: default;
+ opacity: 0.5;
+ }
+ .semantic-passport-reach-actions button[aria-pressed="true"] {
+ border-color: color-mix(in srgb, var(--reach-color, var(--frontend-stroke)) 72%, var(--toolbar-border));
+ background: color-mix(in srgb, var(--reach-fill, var(--frontend-fill)) 48%, transparent);
+ color: var(--text);
+ }
+ #btn-reach-upstream {
+ --reach-color: var(--database-stroke);
+ --reach-fill: var(--database-fill);
+ }
+ #btn-reach-downstream {
+ --reach-color: var(--backend-stroke);
+ --reach-fill: var(--backend-fill);
+ }
+ .semantic-passport-reach-actions strong {
+ color: var(--reach-color, var(--frontend-stroke));
+ font-size: 0.625rem;
+ }
+ .semantic-passport-reach-status {
+ color: var(--text-faint);
+ font-size: 0.525rem;
+ line-height: 1.4;
+ }
+ .semantic-passport-reach-status[hidden] { display: none; }
+ .relationship-lens-list {
+ max-height: min(18rem, 46vh);
+ overflow: auto;
+ padding: 0.4rem;
+ scrollbar-width: thin;
+ scrollbar-color: var(--frontend-stroke) transparent;
+ }
+ .relationship-lens-list::-webkit-scrollbar { width: 4px; }
+ .relationship-lens-list::-webkit-scrollbar-thumb {
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--frontend-stroke) 68%, transparent);
+ }
+ .relationship-lens-group + .relationship-lens-group { margin-top: 0.35rem; }
+ .relationship-lens-group-title {
+ display: block;
+ padding: 0.24rem 0.38rem 0.16rem;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+ .relationship-lens-row {
+ display: grid;
+ grid-template-columns: 2.1rem minmax(0, 1fr);
+ gap: 0.06rem 0.45rem;
+ width: 100%;
+ padding: 0.45rem 0.5rem !important;
+ text-align: left;
+ }
+ .relationship-lens-row:hover,
+ .relationship-lens-row:focus-visible,
+ .relationship-lens-row[data-preview-active="true"] {
+ background: var(--toolbar-hover);
+ box-shadow: inset 2px 0 0 var(--frontend-stroke);
+ }
+ .relationship-lens-row:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: -2px;
+ }
+ .relationship-lens-direction {
+ grid-row: 1 / 3;
+ align-self: center;
+ color: var(--frontend-stroke);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ }
+ .relationship-lens-row[data-direction="in"] .relationship-lens-direction { color: var(--database-stroke); }
+ .relationship-lens-row[data-direction="loop"] .relationship-lens-direction { color: var(--security-stroke); }
+ .relationship-lens-row strong {
+ overflow: hidden;
+ color: var(--text);
+ font-size: 0.6875rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .relationship-lens-row small {
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.5625rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .relationship-lens-empty {
+ margin: 0;
+ padding: 0.75rem;
+ color: var(--text-faint);
+ font-size: 0.625rem;
+ text-align: center;
+ }
+
+ /* Diagram Guide keeps the artifact's growing reader vocabulary
+ discoverable without widening the permanent control bar. It derives
+ honest counts at runtime and delegates every action to an existing
+ production interaction instead of creating a second command system. */
+ .diagram-guide {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ z-index: 26;
+ width: min(32rem, calc(100% - 2rem));
+ max-height: min(42rem, calc(100% - 2rem));
+ overflow: auto;
+ border: 1px solid color-mix(in srgb, var(--cloud-stroke) 58%, var(--toolbar-border));
+ border-radius: 0.82rem;
+ background:
+ linear-gradient(135deg, color-mix(in srgb, var(--cloud-fill) 32%, transparent), transparent 42%),
+ var(--toolbar-menu-bg);
+ color: var(--toolbar-text);
+ box-shadow: 0 22px 64px rgba(0, 0, 0, 0.4);
+ backdrop-filter: blur(18px);
+ animation: archify-guide-in 180ms cubic-bezier(0.22, 1, 0.36, 1);
+ }
+ .diagram-guide[hidden] { display: none; }
+ html[data-preset="blueprint"] .diagram-guide { border-radius: 0.28rem; }
+ .diagram-guide-head {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: start;
+ gap: 1rem;
+ padding: 0.82rem 0.9rem 0.68rem;
+ border-bottom: 1px solid var(--toolbar-border);
+ }
+ .diagram-guide-eyebrow {
+ display: block;
+ color: var(--cloud-stroke);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ }
+ .diagram-guide-title {
+ display: block;
+ margin-top: 0.16rem;
+ color: var(--text);
+ font-size: 0.86rem;
+ letter-spacing: -0.02em;
+ }
+ .diagram-guide-close {
+ width: 1.8rem;
+ height: 1.8rem;
+ border: 0;
+ border-radius: 0.35rem;
+ background: transparent;
+ color: var(--toolbar-text);
+ font: inherit;
+ font-size: 1rem;
+ cursor: pointer;
+ }
+ .diagram-guide-close:hover,
+ .diagram-guide-close:focus-visible { background: var(--toolbar-hover); }
+ .diagram-guide-stats {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin: 0;
+ padding: 0.54rem 0.9rem;
+ border-bottom: 1px solid color-mix(in srgb, var(--toolbar-border) 74%, transparent);
+ color: var(--text-faint);
+ font-size: 0.56rem;
+ letter-spacing: 0.025em;
+ }
+ .diagram-guide-stats::before {
+ content: '';
+ width: 0.42rem;
+ height: 0.42rem;
+ flex: none;
+ border-radius: 50%;
+ background: var(--backend-stroke);
+ box-shadow: 0 0 10px color-mix(in srgb, var(--backend-stroke) 72%, transparent);
+ }
+ .diagram-guide-actions {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0.38rem;
+ padding: 0.62rem;
+ }
+ .diagram-guide-action {
+ --guide-accent: var(--frontend-stroke);
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 0.55rem;
+ min-height: 4.15rem;
+ padding: 0.55rem 0.58rem;
+ border: 1px solid color-mix(in srgb, var(--guide-accent) 22%, var(--toolbar-border));
+ border-radius: 0.55rem;
+ background: color-mix(in srgb, var(--panel) 78%, transparent);
+ color: var(--toolbar-text);
+ font: inherit;
+ text-align: left;
+ cursor: pointer;
+ transition: border-color 140ms ease, background 140ms ease, transform 140ms ease;
+ }
+ .diagram-guide-action[data-guide-action="route"] { --guide-accent: var(--backend-stroke); }
+ .diagram-guide-action[data-guide-action="map"] { --guide-accent: var(--database-stroke); }
+ .diagram-guide-action[data-guide-action="lens"] { --guide-accent: var(--messagebus-stroke); }
+ .diagram-guide-action[data-guide-action="story"] { --guide-accent: var(--security-stroke); }
+ .diagram-guide-action[data-guide-action="present"] {
+ --guide-accent: var(--cloud-stroke);
+ }
+ .diagram-guide-action:hover,
+ .diagram-guide-action:focus-visible {
+ border-color: color-mix(in srgb, var(--guide-accent) 76%, var(--toolbar-border));
+ background: color-mix(in srgb, var(--guide-accent) 9%, var(--panel));
+ transform: translateY(-1px);
+ }
+ .diagram-guide-action:focus-visible,
+ .diagram-guide-close:focus-visible {
+ outline: 2px solid var(--cloud-stroke);
+ outline-offset: 1px;
+ }
+ .diagram-guide-action:disabled {
+ opacity: 0.42;
+ cursor: default;
+ transform: none;
+ }
+ .diagram-guide-index {
+ display: grid;
+ place-items: center;
+ width: 1.65rem;
+ height: 1.65rem;
+ border: 1px solid color-mix(in srgb, var(--guide-accent) 64%, var(--toolbar-border));
+ border-radius: 0.35rem;
+ color: var(--guide-accent);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.06em;
+ }
+ .diagram-guide-action-copy { min-width: 0; }
+ .diagram-guide-action strong {
+ display: block;
+ overflow: hidden;
+ color: var(--text);
+ font-size: 0.66rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .diagram-guide-action small {
+ display: -webkit-box;
+ margin-top: 0.18rem;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ line-height: 1.35;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ }
+ .diagram-guide kbd {
+ align-self: start;
+ min-width: 1.45rem;
+ padding: 0.12rem 0.3rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.25rem;
+ color: var(--text-faint);
+ font: inherit;
+ font-size: 0.5rem;
+ text-align: center;
+ }
+ .diagram-guide-shortcuts {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.34rem 0.62rem;
+ padding: 0.58rem 0.9rem 0.68rem;
+ border-top: 1px solid var(--toolbar-border);
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ }
+ .diagram-guide-shortcuts span {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.28rem;
+ white-space: nowrap;
+ }
+ .diagram-guide-feedback {
+ min-height: 1rem;
+ margin: -0.18rem 0.9rem 0.62rem;
+ color: var(--security-stroke);
+ font-size: 0.5rem;
+ }
+ .diagram-guide-feedback:empty { display: none; }
+ @keyframes archify-guide-in {
+ from { opacity: 0; transform: translateY(-5px) scale(0.985); }
+ to { opacity: 1; transform: translateY(0) scale(1); }
+ }
+
+ .node-finder {
+ position: absolute;
+ top: 1rem;
+ left: 1rem;
+ z-index: 24;
+ display: flex;
+ flex-direction: column;
+ width: min(22rem, calc(100% - 2rem));
+ max-height: calc(100% - 2rem);
+ overflow: hidden;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.85rem;
+ background: var(--toolbar-menu-bg);
+ color: var(--toolbar-text);
+ box-shadow: 0 6px 8px rgba(0, 0, 0, 0.22);
+ backdrop-filter: blur(16px);
+ }
+ .node-finder[hidden] { display: none; }
+ .node-finder[data-context="route-source"] {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 68%, var(--toolbar-border));
+ }
+ .node-finder[data-context="route-target"] {
+ border-color: color-mix(in srgb, var(--security-stroke) 68%, var(--toolbar-border));
+ }
+ html[data-preset="blueprint"] .node-finder { border-radius: 0.3rem; }
+ .node-finder-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.7rem 0.8rem 0.45rem;
+ }
+ .node-finder-head strong {
+ color: var(--text);
+ font-size: 0.6875rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ }
+ .node-finder[data-context="route-source"] .node-finder-head strong { color: var(--frontend-stroke); }
+ .node-finder[data-context="route-target"] .node-finder-head strong { color: var(--security-stroke); }
+ .node-finder-close {
+ width: 1.75rem;
+ height: 1.75rem;
+ border: 0;
+ border-radius: 0.35rem;
+ background: transparent;
+ color: var(--toolbar-text);
+ font: inherit;
+ font-size: 1rem;
+ cursor: pointer;
+ }
+ .node-finder-close:hover,
+ .node-finder-close:focus-visible { background: var(--toolbar-hover); }
+ .node-finder-close:focus-visible,
+ .node-finder-result:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: -2px;
+ }
+ .node-finder-search {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 0.55rem;
+ margin: 0 0.55rem 0.55rem;
+ padding: 0 0.7rem;
+ min-height: 2.6rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.55rem;
+ background: color-mix(in srgb, var(--panel) 78%, transparent);
+ transition: border-color 160ms ease-out, box-shadow 160ms ease-out;
+ }
+ html[data-preset="blueprint"] .node-finder-search { border-radius: 0.18rem; }
+ .node-finder-search > span { color: var(--frontend-stroke); font-size: 0.875rem; }
+ .node-finder-search:focus-within {
+ border-color: var(--frontend-stroke);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--frontend-stroke) 20%, transparent);
+ }
+ .node-finder-input {
+ appearance: none;
+ width: 100%;
+ min-width: 0;
+ border: 0;
+ outline: 0;
+ background: transparent;
+ color: var(--text);
+ font: inherit;
+ font-size: 0.75rem;
+ }
+ .node-finder-input:focus-visible { outline: none; }
+ .node-finder-input::placeholder { color: var(--text-faint); }
+ .node-finder kbd {
+ padding: 0.1rem 0.32rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.25rem;
+ color: var(--text-faint);
+ font: inherit;
+ font-size: 0.5625rem;
+ }
+ .node-finder-results {
+ display: grid;
+ flex: 1 1 auto;
+ gap: 0;
+ min-height: 0;
+ max-height: min(18rem, 48vh);
+ margin: 0 0.55rem 0.45rem;
+ padding: 0;
+ overflow-y: auto;
+ overscroll-behavior: contain;
+ border: 1px solid color-mix(in srgb, var(--toolbar-border) 78%, transparent);
+ border-radius: 0.55rem;
+ background: color-mix(in srgb, var(--panel) 54%, transparent);
+ scroll-padding-block: 0.25rem;
+ scrollbar-width: thin;
+ }
+ html[data-preset="blueprint"] .node-finder-results { border-radius: 0.18rem; }
+ .node-finder-result {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 0.2rem 0.75rem;
+ width: 100%;
+ min-height: 3.25rem;
+ padding: 0.58rem 0.65rem;
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+ color: var(--text);
+ font: inherit;
+ text-align: left;
+ cursor: pointer;
+ transition: background-color 160ms ease-out;
+ }
+ .node-finder-result:not(:last-child) {
+ border-bottom: 1px solid color-mix(in srgb, var(--toolbar-border) 62%, transparent);
+ }
+ .node-finder-result:hover,
+ .node-finder-result:focus-visible {
+ position: relative;
+ z-index: 1;
+ background: color-mix(in srgb, var(--frontend-fill) 48%, var(--toolbar-hover));
+ }
+ .node-finder-result strong {
+ overflow: hidden;
+ font-size: 0.75rem;
+ line-height: 1.25;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .node-finder-result small {
+ grid-column: 1 / -1;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.5625rem;
+ letter-spacing: 0.015em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .node-finder-result em {
+ align-self: center;
+ color: var(--frontend-stroke);
+ font-size: 0.625rem;
+ font-style: normal;
+ }
+ .node-finder[data-context^="route-"] .node-finder-result em {
+ padding: 0.1rem 0.3rem;
+ border: 1px solid color-mix(in srgb, currentColor 52%, transparent);
+ border-radius: 999px;
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ }
+ .node-finder[data-context="route-target"] .node-finder-result em { color: var(--security-stroke); }
+ .node-finder-empty {
+ padding: 1.1rem 0.75rem 1.25rem;
+ color: var(--text-faint);
+ font-size: 0.6875rem;
+ text-align: center;
+ }
+ .node-finder-status {
+ margin: 0;
+ padding: 0.1rem 0.8rem 0.65rem;
+ color: var(--text-faint);
+ font-size: 0.5625rem;
+ }
+
+ .guided-views {
+ display: grid;
+ grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem auto;
+ align-items: stretch;
+ gap: 0.35rem;
+ min-width: 0;
+ margin: 0 0 0.75rem;
+ padding: 0.35rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.85rem;
+ background: var(--toolbar-bg);
+ background: linear-gradient(120deg, var(--toolbar-bg), color-mix(in srgb, var(--toolbar-bg) 82%, var(--frontend-fill)));
+ box-shadow: 0 12px 36px rgba(0, 0, 0, 0.16);
+ backdrop-filter: blur(12px);
+ }
+ .guided-views[hidden] { display: none; }
+
+ html[data-preset="blueprint"] .guided-views {
+ border-color: var(--panel-border);
+ border-radius: 0.35rem;
+ background:
+ linear-gradient(90deg, color-mix(in srgb, var(--frontend-fill) 36%, transparent), transparent 24%),
+ var(--toolbar-bg);
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12);
+ }
+ html[data-preset="blueprint"] .guided-views button { border-radius: 0.2rem; }
+ html[data-preset="blueprint"] .guided-view-progress { border-radius: 0; }
+ html[data-preset="blueprint"] .guided-view-progress span { border-radius: 0; }
+ .guided-views button {
+ min-width: 0;
+ padding: 0.45rem 0.6rem;
+ border-radius: 0.55rem;
+ font-size: 0.75rem;
+ }
+ .guided-views > #guided-view-prev,
+ .guided-views > #guided-view-next { min-width: 2.75rem; min-height: 2.75rem; }
+ .guided-views button:disabled { opacity: 0.3; cursor: default; }
+ .guided-view-copy {
+ min-width: 0;
+ padding: 0.2rem 0.5rem;
+ border-left: 1px solid var(--toolbar-border);
+ overflow: hidden;
+ }
+ .guided-view-meta {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ min-height: 1rem;
+ margin-bottom: 0.12rem;
+ color: var(--frontend-stroke);
+ font-size: 0.5625rem;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+ .guided-view-handoff {
+ box-sizing: border-box;
+ height: 1rem;
+ min-width: 0;
+ max-width: 15rem;
+ overflow: hidden;
+ padding: 0 0.38rem;
+ border: 1px solid color-mix(in srgb, var(--frontend-stroke) 46%, transparent);
+ border-radius: 999px;
+ color: var(--toolbar-text);
+ background: color-mix(in srgb, var(--frontend-fill) 44%, transparent);
+ font-size: 0.5rem;
+ line-height: calc(1rem - 2px);
+ letter-spacing: 0.06em;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .guided-view-handoff[hidden] { display: none; }
+ html[data-preset="blueprint"] .guided-view-handoff { border-radius: 0.15rem; }
+ .chapter-handoff-overlay { pointer-events: none; }
+ .chapter-handoff-anchor {
+ fill: color-mix(in srgb, var(--frontend-fill) 10%, transparent);
+ stroke: var(--frontend-stroke);
+ stroke-width: 2;
+ stroke-dasharray: 4 4;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.96;
+ animation: archify-chapter-anchor 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
+ }
+ svg[data-chapter-handoff] [data-node-id][data-chapter-role="stay"] { opacity: 1; }
+ svg[data-chapter-handoff] [data-node-id][data-chapter-role="enter"] { opacity: 0.78; }
+ svg[data-chapter-handoff] [data-node-id][data-chapter-role="leave"] { opacity: 0.26; }
+ @keyframes archify-chapter-anchor {
+ 0% { opacity: 0.35; stroke-dashoffset: 8; }
+ 45% { opacity: 1; }
+ 100% { opacity: 0.72; stroke-dashoffset: 0; }
+ }
+ .guided-view-progress {
+ width: 3.5rem;
+ height: 2px;
+ overflow: hidden;
+ border-radius: 999px;
+ background: var(--toolbar-border);
+ }
+ .guided-view-progress span {
+ display: block;
+ width: 100%;
+ height: 100%;
+ border-radius: inherit;
+ background: var(--frontend-stroke);
+ transform: scaleX(0);
+ transform-origin: left center;
+ }
+ .guided-view-copy strong,
+ .guided-view-copy small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+ .guided-view-copy strong { color: var(--toolbar-text); font-size: 0.75rem; line-height: 1.35; }
+ .guided-view-copy small { margin-top: 0; color: var(--text-muted); font-size: 0.625rem; line-height: 1.35; }
+ .guided-story-caption {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr) auto;
+ gap: 0.12rem 0.55rem;
+ align-items: center;
+ min-width: 0;
+ margin-top: 0.18rem;
+ padding: 0.3rem 0.5rem;
+ overflow: hidden;
+ border: 1px solid color-mix(in srgb, var(--frontend-stroke) 32%, var(--toolbar-border));
+ border-radius: 0.58rem;
+ background: color-mix(in srgb, var(--frontend-fill) 26%, transparent);
+ animation: archify-story-caption-in 140ms cubic-bezier(0.22, 1, 0.36, 1) both;
+ }
+ .guided-story-caption[hidden] { display: none; }
+ .guided-story-caption-index {
+ grid-row: 1 / span 2;
+ align-self: stretch;
+ display: inline-grid;
+ min-width: 3.3rem;
+ place-items: center;
+ padding-right: 0.52rem;
+ border-right: 1px solid color-mix(in srgb, var(--frontend-stroke) 28%, var(--toolbar-border));
+ color: var(--frontend-stroke);
+ font-size: 0.5625rem;
+ font-weight: 800;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ }
+ .guided-story-caption strong,
+ .guided-story-caption small {
+ display: block;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .guided-story-caption strong { color: var(--toolbar-text); font-size: 0.6875rem; line-height: 1.3; }
+ .guided-story-caption small { color: var(--text-muted); font-size: 0.5625rem; line-height: 1.35; }
+ .guided-story-caption-next {
+ grid-column: 3;
+ grid-row: 1 / span 2;
+ display: grid;
+ min-width: 6.4rem;
+ max-width: 9.5rem;
+ align-self: stretch;
+ align-content: center;
+ padding-left: 0.58rem;
+ border-left: 1px solid color-mix(in srgb, var(--frontend-stroke) 22%, var(--toolbar-border));
+ }
+ .guided-story-caption-next[hidden] { display: none; }
+ .guided-story-caption-next small {
+ color: color-mix(in srgb, var(--frontend-stroke) 72%, var(--text-muted));
+ font-weight: 800;
+ letter-spacing: 0.09em;
+ text-transform: uppercase;
+ }
+ .guided-story-caption-next strong { color: var(--text-muted); font-weight: 700; }
+ html[data-preset="blueprint"] .guided-story-caption { border-radius: 0.2rem; }
+ .guided-views[data-story-beat] .guided-view-copy > #guided-view-label,
+ .guided-views[data-story-beat] .guided-view-copy > #guided-view-note { display: none; }
+ @keyframes archify-story-caption-in {
+ from { opacity: 0; transform: translateY(3px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+ .guided-view-trail {
+ display: flex;
+ align-items: center;
+ gap: 0.28rem;
+ min-width: 0;
+ min-height: 1.7rem;
+ margin-top: 0;
+ padding: 0;
+ overflow-x: auto;
+ color: var(--text-faint);
+ overscroll-behavior-x: contain;
+ touch-action: pan-x;
+ scrollbar-width: none;
+ white-space: nowrap;
+ }
+ .guided-view-trail[hidden] { display: none; }
+ .guided-view-trail::-webkit-scrollbar { display: none; }
+ .guided-view-trail .guided-view-stop {
+ appearance: none;
+ box-sizing: border-box;
+ display: inline-flex;
+ flex: 0 0 auto;
+ align-items: center;
+ gap: 0.28rem;
+ min-height: 1.5rem;
+ padding: 0.1rem 0.3rem 0.1rem 0.18rem;
+ border: 1px solid transparent;
+ border-radius: 999px;
+ background: transparent;
+ color: var(--text-muted);
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 0.5625rem;
+ font-weight: 650;
+ letter-spacing: 0.02em;
+ line-height: 1;
+ transition: background 140ms ease, border-color 140ms ease, color 140ms ease, opacity 140ms ease;
+ }
+ .guided-view-stop:hover {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 36%, transparent);
+ background: color-mix(in srgb, var(--frontend-fill) 34%, transparent);
+ }
+ .guided-view-stop:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: 1px;
+ }
+ .guided-view-stop:disabled { cursor: wait; }
+ .guided-view-stop::before {
+ display: inline-grid;
+ width: 1rem;
+ height: 1rem;
+ place-items: center;
+ border: 1px solid color-mix(in srgb, var(--frontend-stroke) 54%, var(--toolbar-border));
+ border-radius: 50%;
+ background: color-mix(in srgb, var(--frontend-fill) 58%, var(--toolbar-bg));
+ color: var(--frontend-stroke);
+ content: attr(data-story-number);
+ font-size: 0.5rem;
+ line-height: 1;
+ }
+ .guided-view-stop + .guided-view-stop::after {
+ order: -1;
+ color: color-mix(in srgb, var(--frontend-stroke) 58%, var(--text-faint));
+ content: '\00b7';
+ font-size: 0.65rem;
+ }
+ .guided-view-stop[data-story-link="forward"]::after { content: '\2192'; }
+ .guided-view-stop[data-story-link="reverse"]::after { content: '\2190'; }
+ .guided-view-stop[data-story-link="multiple"]::after { content: '\21c4'; }
+ .guided-view-stop[data-story-beat-state="pending"] { opacity: 0.38; }
+ .guided-view-stop[data-story-beat-state="next"] {
+ border-style: dashed;
+ border-color: color-mix(in srgb, var(--frontend-stroke) 42%, var(--toolbar-border));
+ color: color-mix(in srgb, var(--toolbar-text) 68%, var(--text-muted));
+ opacity: 0.58;
+ }
+ .guided-view-stop[data-story-beat-state="past"] { color: var(--text-muted); opacity: 0.76; }
+ .guided-view-stop[data-story-beat-state="active"] {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 72%, var(--toolbar-border));
+ background: color-mix(in srgb, var(--frontend-fill) 42%, transparent);
+ color: var(--toolbar-text);
+ font-weight: 800;
+ opacity: 1;
+ }
+ .guided-view-stop[data-story-beat-state="active"]::before {
+ border-color: var(--frontend-stroke);
+ box-shadow: 0 0 10px color-mix(in srgb, var(--frontend-stroke) 54%, transparent);
+ }
+ html[data-preset="blueprint"] .guided-view-stop,
+ html[data-preset="blueprint"] .guided-view-stop::before { border-radius: 0.1rem; }
+ html[data-preset="blueprint"] .guided-view-stop[data-story-beat-state="active"]::before { box-shadow: none; }
+ .guided-view-actions { display: flex; align-items: stretch; gap: 0.2rem; }
+ .guided-view-actions button { white-space: nowrap; }
+ .guided-view-play {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.38rem;
+ min-width: 6.25rem !important;
+ color: var(--frontend-stroke) !important;
+ font-size: 0.625rem !important;
+ }
+ .guided-view-play[aria-pressed="true"] { background: var(--toolbar-hover); }
+ .guided-view-all { min-width: 3.25rem !important; color: var(--text-muted) !important; font-size: 0.625rem !important; }
+ .guided-view-beat-link {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.3rem;
+ min-width: 5.4rem !important;
+ min-height: 1.5rem;
+ color: var(--text-muted) !important;
+ font-size: 0.5625rem !important;
+ letter-spacing: 0.025em;
+ transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
+ }
+ .guided-view-beat-link:not(:disabled):hover {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 52%, var(--toolbar-border));
+ color: var(--frontend-stroke) !important;
+ }
+ .guided-view-beat-link[data-copy-state="copied"] {
+ border-color: color-mix(in srgb, var(--backend-stroke) 58%, var(--toolbar-border));
+ background: color-mix(in srgb, var(--backend-fill) 46%, transparent);
+ color: var(--backend-stroke) !important;
+ }
+ .guided-view-index {
+ grid-column: 1 / -1;
+ min-width: 0;
+ padding: 0.28rem 0.12rem 0.08rem;
+ overflow: hidden;
+ border-top: 1px solid color-mix(in srgb, var(--toolbar-border) 72%, transparent);
+ }
+ @media (min-width: 721px) {
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-index,
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-beat-link,
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-all,
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-trail,
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-copy > #guided-view-label,
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-view-copy > #guided-view-note { display: none; }
+ html[data-present="true"]:not([data-embed="true"]) .guided-views[data-playing="true"] .guided-story-caption { margin-top: 0.08rem; }
+ }
+ .guided-view-chapters {
+ display: flex;
+ gap: 0.28rem;
+ min-width: 0;
+ margin: 0;
+ padding: 0.08rem 0.05rem 0.2rem;
+ overflow-x: auto;
+ list-style: none;
+ scroll-padding-inline: 0.05rem;
+ scroll-snap-type: x proximity;
+ scrollbar-width: none;
+ }
+ .guided-view-chapters::-webkit-scrollbar { display: none; }
+ .guided-view-chapters li {
+ display: flex;
+ flex: 1 1 0;
+ min-width: 9.5rem;
+ scroll-snap-align: center;
+ }
+ .guided-view-chapter {
+ position: relative;
+ display: grid !important;
+ grid-template-columns: 1.55rem minmax(0, 1fr) auto;
+ align-items: center;
+ width: 100%;
+ min-height: 2.75rem;
+ padding: 0.36rem 0.48rem !important;
+ overflow: hidden;
+ border: 1px solid color-mix(in srgb, var(--toolbar-border) 86%, transparent) !important;
+ border-radius: 0.48rem !important;
+ background: color-mix(in srgb, var(--toolbar-bg) 88%, transparent) !important;
+ color: var(--text-muted) !important;
+ text-align: left;
+ transition: background 0.16s ease, border-color 0.16s ease, opacity 0.16s ease;
+ }
+ .guided-view-chapter:hover {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 58%, var(--toolbar-border)) !important;
+ background: color-mix(in srgb, var(--frontend-fill) 42%, var(--toolbar-bg)) !important;
+ }
+ .guided-view-chapter:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: 1px;
+ }
+ .guided-view-chapter-index {
+ display: inline-grid;
+ width: 1.32rem;
+ height: 1.32rem;
+ place-items: center;
+ border: 1px solid color-mix(in srgb, var(--frontend-stroke) 48%, var(--toolbar-border));
+ border-radius: 50%;
+ color: var(--frontend-stroke);
+ font-size: 0.5rem;
+ font-weight: 800;
+ letter-spacing: 0.03em;
+ }
+ .guided-view-chapter-title {
+ min-width: 0;
+ overflow: hidden;
+ color: var(--toolbar-text);
+ font-size: 0.625rem;
+ font-weight: 700;
+ line-height: 1.3;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .guided-view-chapter-count {
+ grid-column: 3;
+ grid-row: 1;
+ margin-left: 0.38rem;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ font-style: normal;
+ font-weight: 650;
+ white-space: nowrap;
+ }
+ .guided-view-chapter-delta {
+ display: inline-flex;
+ grid-column: 3;
+ grid-row: 1;
+ align-items: center;
+ gap: 0.22rem;
+ margin-left: 0.38rem;
+ color: var(--text-faint);
+ font-size: 0.5rem;
+ font-weight: 750;
+ letter-spacing: 0.01em;
+ white-space: nowrap;
+ }
+ .guided-view-chapter-delta[hidden] { display: none; }
+ .guided-view-chapter-delta [data-delta-kind="stay"] { color: var(--text-muted); }
+ .guided-view-chapter-delta [data-delta-kind="enter"] { color: var(--frontend-stroke); }
+ .guided-view-chapter-delta [data-delta-kind="leave"] { color: var(--security-stroke); }
+ .guided-view-chapter[data-preview-active="true"] {
+ border-color: color-mix(in srgb, var(--frontend-stroke) 72%, var(--toolbar-border)) !important;
+ background: color-mix(in srgb, var(--frontend-fill) 54%, var(--toolbar-bg)) !important;
+ }
+ .guided-view-chapter[data-chapter-position="current"] {
+ border: 2px solid var(--frontend-stroke) !important;
+ padding: calc(0.36rem - 1px) calc(0.48rem - 1px) !important;
+ box-shadow: inset 3px 0 0 var(--frontend-stroke);
+ color: var(--toolbar-text) !important;
+ }
+ .guided-view-chapter[data-chapter-position="current"] .guided-view-chapter-index {
+ background: var(--frontend-stroke);
+ color: var(--bg);
+ }
+ .guided-view-chapter[data-chapter-position="before"] {
+ border-style: solid !important;
+ opacity: 0.72;
+ }
+ .guided-view-chapter[data-chapter-position="after"] { border-style: dashed !important; }
+ html[data-preset="signal-flow"] .guided-view-chapter[data-chapter-position="current"] {
+ box-shadow: inset 3px 0 0 var(--frontend-stroke), 0 0 18px color-mix(in srgb, var(--frontend-stroke) 18%, transparent);
+ }
+ html[data-preset="blueprint"] .guided-view-chapter,
+ html[data-preset="blueprint"] .guided-view-chapter-index { border-radius: 0.12rem !important; }
+
+ /* Story Shelf — keep every authored chapter and the Play action visible,
+ but do not spend a second row on unavailable director controls before
+ the reader enters a chapter. Existing data-active-view state expands
+ the full guided surface without a new toggle or stored preference. */
+ .guided-views[data-active-view="all"] {
+ grid-template-columns: minmax(11rem, .72fr) auto minmax(0, 2fr);
+ align-items: center;
+ padding: 0.28rem 0.35rem;
+ }
+ .guided-views[data-active-view="all"] > #guided-view-prev,
+ .guided-views[data-active-view="all"] > #guided-view-next,
+ .guided-views[data-active-view="all"] .guided-view-beat-link,
+ .guided-views[data-active-view="all"] .guided-view-all { display: none; }
+ .guided-views[data-active-view="all"] .guided-view-copy {
+ grid-column: 1;
+ grid-row: 1;
+ padding: 0.1rem 0.55rem;
+ }
+ .guided-views[data-active-view="all"] .guided-view-copy > #guided-view-note { display: none; }
+ .guided-views[data-active-view="all"] .guided-view-actions {
+ display: block;
+ grid-column: 2;
+ grid-row: 1;
+ }
+ .guided-views[data-active-view="all"] .guided-view-play { min-height: 2.75rem; }
+ .guided-views[data-active-view="all"] .guided-view-index {
+ grid-column: 3;
+ grid-row: 1;
+ padding: 0 0.08rem 0 0.42rem;
+ border-top: 0;
+ border-left: 1px solid color-mix(in srgb, var(--toolbar-border) 72%, transparent);
+ }
+ .guided-views[data-active-view="all"] .guided-view-chapters {
+ gap: 0.3rem;
+ padding: 0;
+ }
+
+ .cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 1rem;
+ margin-top: 2rem;
+ }
+
+ .card {
+ background: var(--panel);
+ border-radius: 0.75rem;
+ border: 1px solid var(--panel-border);
+ padding: 1.25rem;
+ }
+
+ html[data-preset="signal-flow"] .card {
+ background: linear-gradient(145deg, rgba(10, 24, 43, 0.86), rgba(5, 12, 24, 0.92));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035);
+ }
+ html[data-preset="signal-flow"][data-theme="light"] .card {
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.92), rgba(237, 246, 250, 0.9));
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82), 0 12px 32px rgba(45, 78, 99, 0.08);
+ }
+
+ /* Preserve the one-screen guarantee on ordinary laptop displays. The
+ high-screen expansion above must not trade a fuller canvas for page
+ scrolling at 900–1080px heights. */
+ @media (min-width: 768px) and (max-height: 1100px) {
+ .header { margin-bottom: 1rem; }
+ .diagram-container { padding: 0.875rem; }
+ .cards { margin-top: 1rem; }
+ .card { padding: 1rem; }
+ }
+ @media (min-width: 768px) and (max-height: 920px) {
+ .header { margin-bottom: 0.875rem; }
+ .cards { margin-top: 0.75rem; }
+ }
+
+ html[data-preset="blueprint"] .card {
+ position: relative;
+ border-color: var(--panel-border);
+ border-radius: 0.3rem;
+ background: var(--panel);
+ box-shadow: none;
+ }
+ html[data-preset="blueprint"] .card::before {
+ content: "";
+ position: absolute;
+ top: -1px;
+ left: 1rem;
+ width: 2.5rem;
+ border-top: 2px solid var(--frontend-stroke);
+ }
+
+ html[data-preset="editorial"] .card {
+ position: relative;
+ border-radius: 0.28rem;
+ background: var(--panel);
+ box-shadow: 0 10px 28px color-mix(in srgb, #000 10%, transparent);
+ }
+ html[data-preset="editorial"] .card::before {
+ position: absolute;
+ top: -1px;
+ left: 1.25rem;
+ width: 3.25rem;
+ border-top: 3px solid var(--arrow-emphasis);
+ content: "";
+ }
+ html[data-preset="editorial"] .card-dot { border-radius: 0; transform: rotate(45deg) scale(0.78); }
+
+ .card-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.75rem;
+ }
+
+ .card-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ }
+
+ .card-dot.cyan { background: var(--frontend-stroke); }
+ .card-dot.emerald { background: var(--backend-stroke); }
+ .card-dot.violet { background: var(--database-stroke); }
+ .card-dot.amber { background: var(--cloud-stroke); }
+ .card-dot.rose { background: var(--security-stroke); }
+ .card-dot.orange { background: var(--messagebus-stroke); }
+ .card-dot.slate { background: var(--external-stroke); }
+
+ .card h3 { font-size: 0.875rem; font-weight: 600; color: var(--text); }
+
+ .card ul {
+ list-style: none;
+ color: var(--text-muted);
+ font-size: 0.75rem;
+ }
+
+ .card li { margin-bottom: 0.375rem; }
+
+ /* ==========================================================
+ Print stylesheet — hide interactive controls, force light,
+ drop the grid so the diagram doesn't waste ink, use landscape
+ so wide diagrams fit, pack summary cards into 2 columns to
+ avoid an orphan card on a trailing page.
+ ========================================================== */
+ @page { size: landscape; margin: 1.5cm; }
+
+ @media print {
+ /* Force the FULL light palette — including component fills/strokes and
+ lane/arrow colors — so printing from dark theme doesn't put neon
+ strokes and translucent dark fills on white paper. */
+ :root, [data-theme="dark"], [data-theme="light"] {
+ --bg: #ffffff;
+ --grid: transparent;
+ --panel: #ffffff;
+ --panel-border: #e2e8f0;
+ --text: #0f172a;
+ --text-muted: #475569;
+ --text-dim: #94a3b8;
+ --text-faint: #64748b;
+ --mask: #ffffff;
+ --lane-fill: rgba(248, 250, 252, 0.65);
+ --lane-stroke: #cbd5e1;
+ --arrow: #94a3b8;
+ --arrow-emphasis: #059669;
+ --frontend-fill: rgba(34, 211, 238, 0.15);
+ --frontend-stroke: #0891b2;
+ --backend-fill: rgba(52, 211, 153, 0.18);
+ --backend-stroke: #059669;
+ --database-fill: rgba(167, 139, 250, 0.2);
+ --database-stroke: #7c3aed;
+ --cloud-fill: rgba(251, 191, 36, 0.18);
+ --cloud-stroke: #d97706;
+ --security-fill: rgba(251, 113, 133, 0.15);
+ --security-stroke: #e11d48;
+ --messagebus-fill: rgba(251, 146, 60, 0.15);
+ --messagebus-stroke:#ea580c;
+ --external-fill: rgba(148, 163, 184, 0.18);
+ --external-stroke: #64748b;
+ }
+ body { background: #ffffff !important; padding: 0; }
+ .container { max-width: none; }
+ .toolbar, .diagram-nav, .focus-chip, .guided-views, .archify-toast, .no-print { display: none !important; }
+ .chapter-handoff-overlay { display: none !important; }
+ svg [data-node-id][data-chapter-role] { opacity: 1 !important; }
+ svg[data-chapter-preview] [data-node-id],
+ svg[data-chapter-preview] [data-edge-from] { opacity: 1 !important; filter: none !important; }
+ .diagram-container, .card { box-shadow: none; border-color: #e2e8f0; }
+ html[data-preset="signal-flow"] .diagram-container,
+ html[data-preset="signal-flow"] .card { background: #ffffff; }
+ .cards { grid-template-columns: 1fr 1fr; }
+ /* Ensure elements break sensibly across pages */
+ .card { break-inside: avoid; page-break-inside: avoid; }
+ .diagram-container { break-inside: avoid; page-break-inside: avoid; }
+ .diagram-container svg [data-detail] {
+ opacity: 1 !important;
+ pointer-events: auto !important;
+ }
+ .diagram-container svg [data-detail-anchor] { transform: none !important; }
+ .diagram-container svg[data-lens-active] [data-node-id],
+ .diagram-container svg[data-lens-active] [data-edge-from],
+ .diagram-container svg[data-legend-preview-active] [data-node-id],
+ .diagram-container svg[data-legend-preview-active] [data-edge-from] {
+ opacity: 1 !important;
+ filter: none !important;
+ }
+ [data-legend-bridge-runtime] { display: none !important; }
+ .semantic-lens-overlay { display: none !important; }
+ .relationship-hit-overlay { display: none !important; }
+ .relationship-pulse-overlay { display: none !important; }
+ .route-probe-overlay, .route-journey-overlay { display: none !important; }
+ .story-trail-overlay,
+ .story-carrier-overlay { display: none !important; }
+ .diagram-container svg[data-focus-active] [data-node-id],
+ .diagram-container svg[data-focus-active] [data-edge-from],
+ .diagram-container svg[data-reach-active] [data-node-id],
+ .diagram-container svg[data-reach-active] [data-edge-from],
+ .diagram-container svg[data-story-beat] [data-story-step],
+ .diagram-container svg[data-story-beat] [data-edge-from][data-story-beat-step],
+ .diagram-container svg[data-route-active] [data-node-id],
+ .diagram-container svg[data-route-active] [data-edge-from] {
+ opacity: 1 !important;
+ filter: none !important;
+ animation: none !important;
+ }
+ h1, .subtitle { color: #0f172a; }
+ }
+
+ /* ==========================================================
+ TOOLBAR (theme toggle + export menu)
+ ========================================================== */
+ .toolbar {
+ position: fixed;
+ top: 1rem;
+ right: 1rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ box-shadow: none;
+ z-index: 100;
+ }
+ .toolbar button {
+ background: var(--toolbar-bg);
+ color: var(--toolbar-text);
+ border: 1px solid var(--toolbar-border);
+ padding: 0.5rem 0.875rem;
+ border-radius: 0.625rem;
+ font-family: inherit;
+ font-size: 0.75rem;
+ font-weight: 500;
+ cursor: pointer;
+ white-space: nowrap;
+ backdrop-filter: blur(10px);
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.375rem;
+ min-height: 2.75rem;
+ }
+ .toolbar button:hover {
+ background: var(--toolbar-hover);
+ border-color: color-mix(in srgb, var(--arrow) 62%, var(--toolbar-border));
+ }
+ .toolbar button[aria-expanded="true"],
+ .toolbar button[aria-pressed="true"]:not(#btn-theme) {
+ background: color-mix(in srgb, var(--frontend-stroke) 10%, var(--toolbar-hover));
+ border-color: color-mix(in srgb, var(--frontend-stroke) 48%, var(--toolbar-border));
+ }
+ .toolbar #btn-export {
+ padding-right: 0.82rem;
+ padding-left: 0.82rem;
+ }
+ .toolbar #btn-export:hover,
+ .toolbar #btn-export[aria-expanded="true"] {
+ color: var(--toolbar-text);
+ background: color-mix(in srgb, var(--frontend-stroke) 10%, var(--toolbar-hover));
+ border-color: color-mix(in srgb, var(--frontend-stroke) 54%, var(--toolbar-border));
+ }
+ .toolbar button:focus-visible {
+ outline: 2px solid var(--frontend-stroke);
+ outline-offset: 2px;
+ }
+ .toolbar #btn-motion[hidden] { display: none !important; }
+ .toolbar #btn-motion { min-width: 4.4rem; }
+ .toolbar #btn-motion:disabled { cursor: default; opacity: 0.58; }
+ .toolbar .preset-wrap,
+ .toolbar .export-wrap {
+ position: relative;
+ }
+ .toolbar-icon {
+ display: inline-block;
+ width: 0.95rem;
+ height: 0.95rem;
+ flex: 0 0 auto;
+ background: currentColor;
+ -webkit-mask-position: center;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: contain;
+ mask-position: center;
+ mask-repeat: no-repeat;
+ mask-size: contain;
+ }
+ .toolbar-chevron {
+ width: 0.65rem;
+ height: 0.65rem;
+ transition: transform 0.15s ease;
+ }
+ [aria-expanded="true"] > .toolbar-chevron { transform: rotate(180deg); }
+ [data-theme="light"] #theme-icon {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cg fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'%3E%3Ccircle cx='10' cy='10' r='3.1'/%3E%3Cpath d='M10 2.2v1.4M10 16.4v1.4M2.2 10h1.4M16.4 10h1.4M4.5 4.5l1 1M14.5 14.5l1 1M15.5 4.5l-1 1M5.5 14.5l-1 1'/%3E%3C/g%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cg fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'%3E%3Ccircle cx='10' cy='10' r='3.1'/%3E%3Cpath d='M10 2.2v1.4M10 16.4v1.4M2.2 10h1.4M16.4 10h1.4M4.5 4.5l1 1M14.5 14.5l1 1M15.5 4.5l-1 1M5.5 14.5l-1 1'/%3E%3C/g%3E%3C/svg%3E");
+ }
+ [data-theme="dark"] #theme-icon {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M15.2 12.7A6.3 6.3 0 017.3 4.8a6.3 6.3 0 107.9 7.9Z' fill='none' stroke='black' stroke-width='1.7' stroke-linejoin='round'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M15.2 12.7A6.3 6.3 0 017.3 4.8a6.3 6.3 0 107.9 7.9Z' fill='none' stroke='black' stroke-width='1.7' stroke-linejoin='round'/%3E%3C/svg%3E");
+ }
+ #btn-present[aria-pressed="false"] #present-icon {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M7.2 3.2h-4v4M12.8 3.2h4v4M7.2 16.8h-4v-4M12.8 16.8h4v-4' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M7.2 3.2h-4v4M12.8 3.2h4v4M7.2 16.8h-4v-4M12.8 16.8h4v-4' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
+ }
+ #btn-present[aria-pressed="true"] #present-icon {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M7.2 7.2h-4v-4M12.8 7.2h4v-4M7.2 12.8h-4v4M12.8 12.8h4v4' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M7.2 7.2h-4v-4M12.8 7.2h4v-4M7.2 12.8h-4v4M12.8 12.8h4v4' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
+ }
+ .toolbar-chevron {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='m3 4.5 3 3 3-3' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='m3 4.5 3 3 3-3' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
+ }
+ .toolbar #btn-preset { min-width: 5.4rem; }
+ .preset-control-mark {
+ width: 0.58rem;
+ height: 0.58rem;
+ flex: 0 0 auto;
+ border: 1px solid var(--frontend-stroke);
+ border-radius: 0.2rem;
+ background: color-mix(in srgb, var(--frontend-stroke) 28%, transparent);
+ transition: border-radius 0.15s, box-shadow 0.15s, transform 0.15s;
+ }
+ #btn-preset[data-preset-option="signal-flow"] .preset-control-mark {
+ border-radius: 50%;
+ box-shadow: 0 0 10px color-mix(in srgb, var(--frontend-stroke) 78%, transparent);
+ }
+ #btn-preset[data-preset-option="blueprint"] .preset-control-mark {
+ border-radius: 0.06rem;
+ background: transparent;
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--frontend-stroke) 32%, transparent);
+ transform: rotate(45deg) scale(0.84);
+ }
+ #btn-preset[data-preset-option="editorial"] .preset-control-mark {
+ border-color: var(--arrow-emphasis);
+ border-radius: 0;
+ background: var(--arrow-emphasis);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--arrow-emphasis) 14%, transparent);
+ transform: rotate(45deg) scale(0.72);
+ }
+ .toolbar .preset-menu {
+ position: absolute;
+ top: calc(100% + 0.375rem);
+ right: 0;
+ display: none;
+ width: 18.5rem;
+ max-width: calc(100vw - 2rem);
+ padding: 0.45rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.875rem;
+ background: var(--toolbar-menu-bg);
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.24);
+ }
+ .toolbar .preset-menu.open { display: block; }
+ .toolbar .preset-menu.open,
+ .toolbar .export-menu.open {
+ animation: toolbar-menu-in 0.16s cubic-bezier(0.2, 0.8, 0.2, 1);
+ transform-origin: top right;
+ }
+ @keyframes toolbar-menu-in {
+ from { opacity: 0; transform: translateY(-0.25rem) scale(0.985); }
+ to { opacity: 1; transform: translateY(0) scale(1); }
+ }
+ .preset-menu-heading {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.42rem 0.6rem 0.55rem;
+ color: var(--text-faint);
+ font-size: 0.56rem;
+ letter-spacing: 0.13em;
+ text-transform: uppercase;
+ }
+ .preset-menu-heading span:last-child {
+ font-size: 0.52rem;
+ letter-spacing: 0.04em;
+ text-transform: none;
+ }
+ .toolbar .preset-option {
+ display: grid;
+ grid-template-columns: 2rem minmax(0, 1fr) 1rem;
+ align-items: center;
+ gap: 0.68rem;
+ width: 100%;
+ min-height: 3.35rem;
+ padding: 0.5rem 0.62rem;
+ border: 0;
+ border-radius: 0.5rem;
+ background: transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ text-align: left;
+ }
+ .toolbar .preset-option:hover,
+ .toolbar .preset-option:focus-visible { background: var(--toolbar-hover); }
+ .toolbar .preset-option[aria-checked="true"] {
+ background: color-mix(in srgb, var(--frontend-stroke) 9%, transparent);
+ border-color: color-mix(in srgb, var(--frontend-stroke) 36%, transparent);
+ }
+ .preset-option-swatch {
+ position: relative;
+ width: 2rem;
+ height: 1.55rem;
+ overflow: hidden;
+ border: 1px solid #64748b;
+ border-radius: 0.24rem;
+ background: #0f172a;
+ box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04);
+ }
+ .preset-option-swatch::before,
+ .preset-option-swatch::after { position: absolute; content: ""; }
+ .preset-option-swatch.classic::before {
+ top: 0.42rem;
+ right: 0.35rem;
+ bottom: 0.42rem;
+ left: 0.35rem;
+ border: 1px solid #22d3ee;
+ border-radius: 0.16rem;
+ background: rgba(34, 211, 238, 0.14);
+ }
+ .preset-option-swatch.signal-flow {
+ border-color: #29415f;
+ background: linear-gradient(135deg, #07101e 10%, #10243c 100%);
+ }
+ .preset-option-swatch.signal-flow::before {
+ top: 50%;
+ right: 0.2rem;
+ left: 0.2rem;
+ height: 1px;
+ background: linear-gradient(90deg, #67e8f9, #c4b5fd);
+ box-shadow: 0 0 8px #67e8f9;
+ }
+ .preset-option-swatch.signal-flow::after {
+ top: calc(50% - 0.13rem);
+ right: 0.28rem;
+ width: 0.26rem;
+ height: 0.26rem;
+ border-radius: 50%;
+ background: #67e8f9;
+ box-shadow: 0 0 7px #67e8f9;
+ }
+ .preset-option-swatch.blueprint {
+ border-color: #34799a;
+ border-radius: 0.08rem;
+ background-color: #0a2031;
+ background-image:
+ linear-gradient(rgba(102, 217, 239, 0.18) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(102, 217, 239, 0.18) 1px, transparent 1px);
+ background-size: 0.42rem 0.42rem;
+ }
+ .preset-option-swatch.blueprint::before {
+ top: 0.34rem;
+ right: 0.38rem;
+ bottom: 0.34rem;
+ left: 0.38rem;
+ border: 1px solid #66d9ef;
+ }
+ .preset-option-swatch.editorial {
+ border-color: #b8aa94;
+ border-radius: 0.08rem;
+ background: repeating-linear-gradient(0deg, #f2eee5 0 0.34rem, #d8d0c2 0.36rem, #f2eee5 0.38rem);
+ }
+ .preset-option-swatch.editorial::before {
+ top: 0;
+ bottom: 0;
+ left: 0.42rem;
+ width: 1px;
+ background: #c9502d;
+ opacity: 0.72;
+ }
+ .preset-option-swatch.editorial::after {
+ top: 0.52rem;
+ right: 0.45rem;
+ width: 0.38rem;
+ height: 0.38rem;
+ background: #c9502d;
+ transform: rotate(45deg);
+ }
+ .preset-option-copy { min-width: 0; }
+ .preset-option-copy strong,
+ .preset-option-copy small { display: block; }
+ .preset-option-copy strong {
+ color: var(--toolbar-text);
+ font-size: 0.72rem;
+ font-weight: 600;
+ }
+ .preset-option-copy small {
+ margin-top: 0.16rem;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.57rem;
+ font-weight: 400;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+ .preset-option-check {
+ display: inline-block;
+ width: 0.9rem;
+ height: 0.9rem;
+ color: var(--frontend-stroke);
+ background: currentColor;
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m3 8.5 3 3 7-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
+ -webkit-mask-position: center;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: contain;
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='m3 8.5 3 3 7-7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
+ mask-position: center;
+ mask-repeat: no-repeat;
+ mask-size: contain;
+ opacity: 0;
+ }
+ .preset-option[aria-checked="true"] .preset-option-check { opacity: 1; }
+ html[data-preset="editorial"] .toolbar button,
+ html[data-preset="editorial"] .preset-menu,
+ html[data-preset="editorial"] .export-menu,
+ html[data-preset="editorial"] .overview-map,
+ html[data-preset="editorial"] .semantic-lens,
+ html[data-preset="editorial"] .route-probe,
+ html[data-preset="editorial"] .focus-chip,
+ html[data-preset="editorial"] .diagram-guide,
+ html[data-preset="editorial"] .node-finder,
+ html[data-preset="editorial"] .guided-views,
+ html[data-preset="editorial"] .share-chapter-cue {
+ border-radius: 0.3rem;
+ }
+ .motion-control-dot {
+ width: 0.46rem;
+ height: 0.46rem;
+ flex: 0 0 auto;
+ border: 1px solid var(--frontend-stroke);
+ border-radius: 50%;
+ background: var(--frontend-stroke);
+ box-shadow: 0 0 9px color-mix(in srgb, var(--frontend-stroke) 72%, transparent);
+ }
+ #btn-motion[aria-pressed="false"] .motion-control-dot {
+ background: transparent;
+ box-shadow: none;
+ }
+ .toolbar .export-menu {
+ position: absolute;
+ top: calc(100% + 0.375rem);
+ right: 0;
+ background: var(--toolbar-menu-bg);
+ border: 1px solid var(--toolbar-border);
+ width: 19rem;
+ max-width: calc(100vw - 2rem);
+ max-height: calc(100vh - 5.5rem);
+ overflow-y: auto;
+ border-radius: 0.875rem;
+ padding: 0.55rem;
+ display: none;
+ flex-direction: column;
+ box-shadow: 0 18px 48px rgba(0,0,0,0.24);
+ }
+ .toolbar .export-menu.open { display: flex; }
+ .export-menu-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 0.5rem 0.55rem 0.72rem;
+ }
+ .export-menu-header strong,
+ .export-menu-header span { display: block; }
+ .export-menu-header strong {
+ color: var(--toolbar-text);
+ font-size: 0.78rem;
+ font-weight: 650;
+ letter-spacing: -0.015em;
+ }
+ .export-menu-header span {
+ margin-top: 0.16rem;
+ color: var(--text-faint);
+ font-size: 0.56rem;
+ }
+ .export-menu-header kbd {
+ padding: 0.16rem 0.36rem;
+ border: 1px solid var(--toolbar-border);
+ border-radius: 0.3rem;
+ color: var(--text-faint);
+ background: color-mix(in srgb, var(--toolbar-border) 16%, transparent);
+ font-family: inherit;
+ font-size: 0.54rem;
+ font-weight: 600;
+ }
+ .export-menu-section {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr);
+ gap: 0.18rem;
+ }
+ .export-menu-section + .export-menu-section {
+ margin-top: 0.42rem;
+ padding-top: 0.42rem;
+ border-top: 1px solid color-mix(in srgb, var(--toolbar-border) 68%, transparent);
+ }
+ .export-menu-heading {
+ grid-column: 1 / -1;
+ display: block;
+ padding: 0.24rem 0.55rem 0.2rem;
+ color: var(--text-faint);
+ font-size: 0.54rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+ .toolbar .export-menu button {
+ display: grid;
+ grid-template-columns: 1.15rem minmax(0, 1fr);
+ align-items: center;
+ column-gap: 0.52rem;
+ background: transparent;
+ border: 1px solid transparent;
+ box-shadow: none;
+ backdrop-filter: none;
+ width: 100%;
+ min-height: 3rem;
+ text-align: left;
+ padding: 0.48rem 0.55rem;
+ border-radius: 0.5rem;
+ white-space: nowrap;
+ }
+ .export-item-copy {
+ display: block;
+ min-width: 0;
+ }
+ .export-item-copy strong,
+ .export-item-copy small { display: block; }
+ .export-item-copy strong {
+ overflow: hidden;
+ color: inherit;
+ font-size: 0.7rem;
+ font-weight: 550;
+ text-overflow: ellipsis;
+ }
+ .export-item-copy small {
+ margin-top: 0.12rem;
+ overflow: hidden;
+ color: var(--text-faint);
+ font-size: 0.55rem;
+ font-weight: 450;
+ text-overflow: ellipsis;
+ }
+ .toolbar .export-menu button::before {
+ width: 1rem;
+ height: 1rem;
+ background: currentColor;
+ content: "";
+ opacity: 0.72;
+ -webkit-mask-position: center;
+ -webkit-mask-repeat: no-repeat;
+ -webkit-mask-size: contain;
+ mask-position: center;
+ mask-repeat: no-repeat;
+ mask-size: contain;
+ }
+ .toolbar .export-menu button[data-format="share-card"]::before,
+ .toolbar .export-menu button[data-action="route-share-card"]::before,
+ .toolbar .export-menu button[data-action="reach-share-card"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='black' stroke-width='1.7' d='M3 4.5h14v11H3zM6 12l2.5-2.5 2 2 1.5-1.5 2 2'/%3E%3Ccircle cx='13.8' cy='7.3' r='1.2' fill='black'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='black' stroke-width='1.7' d='M3 4.5h14v11H3zM6 12l2.5-2.5 2 2 1.5-1.5 2 2'/%3E%3Ccircle cx='13.8' cy='7.3' r='1.2' fill='black'/%3E%3C/svg%3E");
+ }
+ .toolbar .export-menu button[data-action^="copy"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='6' y='6' width='10' height='10' rx='1.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M4 13H3.5A1.5 1.5 0 012 11.5v-8A1.5 1.5 0 013.5 2h8A1.5 1.5 0 0113 3.5V4' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='6' y='6' width='10' height='10' rx='1.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M4 13H3.5A1.5 1.5 0 012 11.5v-8A1.5 1.5 0 013.5 2h8A1.5 1.5 0 0113 3.5V4' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
+ .toolbar .export-menu button[data-format="png"]::before,
+ .toolbar .export-menu button[data-format="jpeg"]::before,
+ .toolbar .export-menu button[data-format="webp"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M5 2.5h7l3 3v12H5zM12 2.5v3h3' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M7.5 14l2-2 1.5 1.5 1.5-1.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M5 2.5h7l3 3v12H5zM12 2.5v3h3' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M7.5 14l2-2 1.5 1.5 1.5-1.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
+ .toolbar .export-menu button[data-format="svg"]::before,
+ .toolbar .export-menu button[data-format="webm"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
+ .toolbar .export-menu button[data-format="drawio"]::before,
+ .toolbar .export-menu button[data-format="drawio-strict"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
+ .toolbar .export-menu button[data-format="share-card"],
+ .toolbar .export-menu button[data-action="route-share-card"],
+ .toolbar .export-menu button[data-action="reach-share-card"] {
+ min-height: 3.35rem;
+ color: var(--toolbar-text);
+ background: color-mix(in srgb, var(--frontend-stroke) 8%, transparent);
+ border-color: color-mix(in srgb, var(--frontend-stroke) 24%, var(--toolbar-border));
+ }
+ .toolbar .export-menu button[hidden] { display: none; }
+ .toolbar .export-menu button:hover {
+ background: var(--toolbar-hover);
+ }
+ .toolbar .export-menu button:focus-visible {
+ background: var(--toolbar-hover);
+ outline-offset: -2px;
+ }
+ .toolbar .export-menu button:disabled {
+ color: var(--text-faint);
+ background: color-mix(in srgb, var(--toolbar-border) 18%, transparent);
+ cursor: not-allowed;
+ }
+ .toolbar .export-menu button:disabled:hover {
+ border-color: transparent;
+ }
+ .toolbar .export-menu .hint {
+ color: var(--text-faint);
+ font-size: 0.55rem;
+ }
+
+ /* Toast — brief feedback for actions like "copied" */
+ .archify-toast {
+ position: fixed;
+ top: 1rem;
+ left: 50%;
+ transform: translateX(-50%) translateY(-8px);
+ background: var(--toolbar-menu-bg);
+ color: var(--text);
+ border: 1px solid var(--toolbar-border);
+ padding: 0.5rem 1rem;
+ border-radius: 0.5rem;
+ font-family: inherit;
+ font-size: 0.75rem;
+ opacity: 0;
+ transition: opacity 0.2s, transform 0.2s;
+ z-index: 200;
+ pointer-events: none;
+ box-shadow: 0 4px 20px rgba(0,0,0,0.25);
+ }
+ .archify-toast.show {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0);
+ }
+
+ @media (max-width: 720px) {
+ body { padding: 1rem; }
+ .toolbar .preset-menu,
+ .toolbar .export-menu {
+ position: fixed;
+ top: 4.5rem;
+ right: 1rem;
+ left: 1rem;
+ width: auto;
+ max-width: none;
+ }
+ .toolbar {
+ position: relative;
+ justify-content: flex-end;
+ width: max-content;
+ max-width: 100%;
+ margin-left: auto;
+ margin-bottom: 1rem;
+ }
+ .header { margin-bottom: 1.25rem; padding-right: 0; }
+ html[data-preset="signal-flow"] .header,
+ html[data-preset="blueprint"] .header,
+ html[data-preset="editorial"] .header { padding-right: 0; }
+ .header-row {
+ gap: 0.75rem;
+ align-items: flex-start;
+ }
+ html[data-preset="signal-flow"] .header-row,
+ html[data-preset="blueprint"] .header-row,
+ html[data-preset="editorial"] .header-row { flex-wrap: wrap; }
+ html[data-preset="signal-flow"] .header-row::after {
+ margin-left: 0;
+ order: 3;
+ }
+ html[data-preset="blueprint"] .header-row::after {
+ margin-left: 0;
+ order: 3;
+ }
+ html[data-preset="editorial"] .header-row::after {
+ margin-left: 0;
+ order: 3;
+ }
+ h1 {
+ font-size: 1.25rem;
+ line-height: 1.25;
+ }
+ .subtitle {
+ margin-left: 0;
+ font-size: 0.8125rem;
+ line-height: 1.55;
+ }
+ .diagram-container {
+ padding: 0.75rem;
+ border-radius: 0.75rem;
+ }
+ html:not([data-embed="true"]) .diagram-container {
+ padding: 0.75rem 0.75rem 4.25rem;
+ }
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] {
+ overflow-x: auto;
+ overflow-y: hidden;
+ overscroll-behavior-x: contain;
+ scrollbar-width: thin;
+ scroll-behavior: smooth;
+ }
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] > svg {
+ min-width: 720px;
+ }
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .diagram-nav,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .overview-map,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .focus-chip,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .route-probe,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .semantic-lens,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .diagram-guide,
+ html:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] .node-finder {
+ transform: translateX(var(--archify-scroll-x, 0px));
+ }
+ .diagram-nav { right: 0.5rem; bottom: 0.5rem; }
+ .diagram-nav button {
+ min-width: 2.75rem;
+ height: 2.75rem;
+ }
+ .diagram-nav [data-view="reset"] { min-width: 3.75rem; }
+ .diagram-nav [data-view="reset"][data-detail-visible] { min-width: 4.35rem; }
+ .overview-map {
+ right: 0.5rem;
+ bottom: 3.7rem;
+ width: min(10.25rem, calc(100% - 1rem));
+ }
+ .overview-map[data-docked="true"] {
+ right: 0.5rem;
+ top: var(--archify-radar-top, 0.5rem);
+ bottom: auto;
+ transform: none !important;
+ }
+ .overview-map[data-docked="true"][data-dock-side="left"] {
+ right: auto;
+ left: 0.5rem;
+ }
+ .overview-map-surface { height: 4rem; }
+ .semantic-lens {
+ right: 0.5rem;
+ bottom: 3.7rem;
+ width: calc(100% - 1rem);
+ max-height: calc(100% - 4.2rem);
+ }
+ .semantic-lens[data-dock-side] { left: auto; right: 0.5rem; }
+ .semantic-lens-kinds { max-height: min(12rem, 34vh); }
+ .focus-chip {
+ left: 0.5rem;
+ top: 0.5rem;
+ width: calc(100% - 1rem);
+ max-width: none;
+ }
+ .relationship-lens-list { max-height: min(12rem, 38vh); }
+ .relationship-lens-actions #btn-focus-relations { display: block; color: var(--database-stroke); }
+ .focus-chip:not([data-relations-expanded="true"]) .relationship-lens-head { border-bottom-color: transparent; }
+ .focus-chip:not([data-relations-expanded="true"]) .relationship-lens-list { display: none; }
+ /* While a mobile reader previews one relationship, turn the full Lens
+ into a compact peek card. This keeps the exact source and target on
+ canvas instead of covering them with a long, scrollable list. */
+ .focus-chip[data-relationship-previewing="true"] .relationship-lens-list {
+ max-height: none;
+ padding: 0.35rem;
+ overflow: hidden;
+ }
+ .focus-chip[data-relationship-previewing="true"] .relationship-lens-group { margin: 0; }
+ .focus-chip[data-relationship-previewing="true"] .relationship-lens-group-title,
+ .focus-chip[data-relationship-previewing="true"] .relationship-lens-row:not([data-preview-active="true"]) {
+ display: none;
+ }
+ .node-finder {
+ top: 0.5rem;
+ left: 0.5rem;
+ width: min(22rem, calc(100% - 1rem));
+ max-height: calc(100% - 1rem);
+ }
+ .diagram-guide {
+ top: 0.5rem;
+ right: 0.5rem;
+ left: 0.5rem;
+ width: auto;
+ max-height: calc(100% - 1rem);
+ }
+ .diagram-guide-head { padding: 0.62rem 0.7rem 0.52rem; }
+ .diagram-guide-stats { padding: 0.42rem 0.7rem; }
+ .diagram-guide-actions {
+ grid-template-columns: 1fr;
+ gap: 0.28rem;
+ padding: 0.42rem;
+ }
+ .diagram-guide-action {
+ min-height: 3.1rem;
+ padding: 0.42rem 0.48rem;
+ }
+ .diagram-guide-action small {
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .diagram-guide-index {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
+ .diagram-guide-shortcuts { padding: 0.46rem 0.7rem 0.52rem; }
+ .diagram-guide-action[data-guide-action="present"] { grid-column: auto; }
+ .route-probe[data-guide-open="true"] {
+ visibility: hidden;
+ pointer-events: none;
+ }
+ .node-finder-results { max-height: min(16rem, 38vh); }
+ .route-probe {
+ left: 0.5rem;
+ bottom: 3.7rem;
+ width: calc(100% - 1rem);
+ min-width: 0;
+ }
+ .route-probe[data-route-dock="top"] {
+ top: 0.5rem;
+ bottom: auto;
+ }
+ .route-probe[data-finder-open="true"] {
+ visibility: hidden;
+ pointer-events: none;
+ }
+ .route-probe-head { padding: 0.45rem 0.48rem 0.38rem; }
+ .route-probe-path { padding: 0.35rem 0.48rem 0.2rem; }
+ .route-probe-node { min-height: 2.75rem !important; }
+ .route-journey-controls {
+ grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem minmax(0, 1fr);
+ padding: 0.08rem 0.48rem 0.32rem;
+ }
+ .route-journey-controls button { min-height: 2.75rem; }
+ .route-probe-status { padding: 0.05rem 0.48rem 0.38rem; }
+ .route-probe-node { max-width: 7.5rem; }
+ .guided-views {
+ grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem;
+ gap: 0.2rem;
+ padding: 0.25rem;
+ }
+ .guided-view-actions {
+ grid-column: 1 / -1;
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+ .guided-views > #guided-view-prev,
+ .guided-views > #guided-view-next {
+ align-self: center;
+ height: 2.75rem;
+ }
+ .guided-view-play,
+ .guided-view-all,
+ .guided-view-beat-link { min-height: 2.75rem; }
+ .guided-view-copy small { white-space: normal; }
+ .guided-story-caption { padding: 0.32rem 0.45rem; }
+ .guided-story-caption-index { min-width: 2.9rem; padding-right: 0.42rem; }
+ .guided-story-caption strong,
+ .guided-story-caption small { white-space: normal; }
+ .guided-story-caption-next { display: none; }
+ .guided-view-trail { min-height: 2rem; }
+ .guided-view-trail .guided-view-stop {
+ min-height: 2rem;
+ padding: 0.22rem 0.42rem 0.22rem 0.28rem;
+ }
+ .guided-view-handoff { max-width: min(9rem, 32vw); }
+ .guided-view-index { padding-top: 0.35rem; }
+ .guided-view-chapters {
+ gap: 0.35rem;
+ scroll-padding-inline: 0.2rem;
+ }
+ .guided-view-chapters li {
+ flex: 0 0 min(14rem, 78vw);
+ min-width: 0;
+ }
+ .guided-view-chapter { min-height: 2.75rem; }
+ .guided-views[data-active-view="all"] {
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 0.2rem;
+ padding: 0.25rem;
+ }
+ .guided-views[data-active-view="all"] .guided-view-copy {
+ grid-column: 1;
+ grid-row: 1;
+ }
+ .guided-views[data-active-view="all"] .guided-view-actions {
+ grid-column: 2;
+ grid-row: 1;
+ }
+ .guided-views[data-active-view="all"] .guided-view-play {
+ min-width: 6rem !important;
+ min-height: 2.75rem;
+ }
+ .guided-views[data-active-view="all"] .guided-view-index {
+ grid-column: 1 / -1;
+ grid-row: 2;
+ padding: 0.3rem 0.08rem 0;
+ border-top: 1px solid color-mix(in srgb, var(--toolbar-border) 72%, transparent);
+ border-left: 0;
+ }
+ html[data-embed="true"][data-share-playback="true"] .share-chapter-cue:not([hidden]),
+ html[data-embed="true"][data-share-moment="true"] .share-chapter-cue:not([hidden]) {
+ top: 0.5rem;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 0.28rem 0.55rem;
+ width: calc(100% - 1rem);
+ min-height: 0;
+ padding: 0.45rem 0.55rem 0.55rem;
+ }
+ html[data-embed="true"][data-share-playback="true"] .diagram-container {
+ padding-top: 4.25rem;
+ }
+ html[data-embed="true"][data-share-moment="true"] .diagram-container {
+ padding-top: 4.25rem;
+ }
+ .share-chapter-index {
+ min-width: 4.4rem;
+ padding-right: 0.5rem;
+ }
+ .share-chapter-copy small { font-size: 0.46rem; }
+ .share-chapter-route {
+ grid-column: 1 / -1;
+ font-size: 0.5rem;
+ text-align: left;
+ }
+ .cards {
+ grid-template-columns: 1fr;
+ margin-top: 1rem;
+ }
+ html[data-present="true"]:not([data-embed="true"]) body { padding: 0.5rem; }
+ html[data-present="true"]:not([data-embed="true"]) .container {
+ gap: 0.5rem;
+ height: calc(100dvh - 1rem);
+ }
+ html[data-present="true"]:not([data-embed="true"]) .toolbar {
+ position: fixed;
+ top: 0.5rem;
+ right: 0.5rem;
+ margin: 0;
+ gap: 0.25rem;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .toolbar button {
+ min-height: 2.75rem;
+ padding: 0.42rem 0.58rem;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .header {
+ min-height: 2.25rem;
+ padding: 3.5rem 0 0;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .header-row {
+ min-height: 2.25rem;
+ margin: 0;
+ align-items: center;
+ flex-wrap: nowrap;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .header-row::after,
+ html[data-present="true"]:not([data-embed="true"]) .pulse-dot,
+ html[data-present="true"]:not([data-embed="true"]) .subtitle { display: none; }
+ html[data-present="true"]:not([data-embed="true"]) h1 {
+ flex: 1 1 auto;
+ min-width: 0;
+ max-width: 100%;
+ overflow: hidden;
+ font-size: 0.875rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .guided-views {
+ padding: 0.2rem;
+ }
+ html[data-present="true"]:not([data-embed="true"]) .guided-view-copy small { display: none; }
+ html[data-present="true"]:not([data-embed="true"]) .guided-story-caption small { display: block; }
+ html[data-present="true"]:not([data-embed="true"]) .diagram-container { padding: 0.5rem 0.5rem 3.75rem; }
+ html[data-present="true"]:not([data-embed="true"]) .diagram-container[data-wide-diagram="true"] > svg {
+ width: 720px;
+ height: auto;
+ min-width: 720px;
+ }
+ }
+
+ @media (max-width: 420px) {
+ .export-menu-section { grid-template-columns: 1fr; }
+ .export-menu-heading,
+ .toolbar .export-menu button[data-format="share-card"],
+ .toolbar .export-menu button[data-action="route-share-card"],
+ .toolbar .export-menu button[data-action="reach-share-card"] { grid-column: 1; }
+ }
+
+ @media (prefers-reduced-motion: reduce) {
+ .guided-story-caption { animation: none !important; }
+ .toolbar .preset-menu.open,
+ .toolbar .export-menu.open { animation: none; }
+ .toolbar-chevron { transition: none; }
+ }
+
+ @media (max-width: 360px) {
+ .toolbar { gap: 0.25rem; }
+ .toolbar button { padding-right: 0.58rem; padding-left: 0.58rem; }
+ .toolbar #btn-motion { min-width: 4.4rem; padding-right: 0.58rem; padding-left: 0.58rem; }
+ #theme-label, #preset-label, #present-label { display: none; }
+ .guided-view-handoff { max-width: 6.5rem; }
+ }
+
+ /* ==========================================================
+ SVG SEMANTIC CLASSES — use these instead of inline fill/stroke
+ ========================================================== */
+ .c-grid { stroke: var(--grid); fill: none; }
+ .c-mask { fill: var(--mask); stroke: none; }
+
+ .c-frontend { fill: var(--frontend-fill); stroke: var(--frontend-stroke); }
+ .c-backend { fill: var(--backend-fill); stroke: var(--backend-stroke); }
+ .c-database { fill: var(--database-fill); stroke: var(--database-stroke); }
+ .c-cloud { fill: var(--cloud-fill); stroke: var(--cloud-stroke); }
+ .c-security { fill: var(--security-fill); stroke: var(--security-stroke); }
+ .c-messagebus { fill: var(--messagebus-fill); stroke: var(--messagebus-stroke); }
+ .c-external { fill: var(--external-fill); stroke: var(--external-stroke); }
+
+ /* Text helpers */
+ .t-primary { fill: var(--text); }
+ .t-muted { fill: var(--text-muted); }
+ .t-dim { fill: var(--text-dim); }
+ .t-frontend { fill: var(--frontend-stroke); }
+ .t-backend { fill: var(--backend-stroke); }
+ .t-database { fill: var(--database-stroke); }
+ .t-cloud { fill: var(--cloud-stroke); }
+ .t-security { fill: var(--security-stroke); }
+ .t-messagebus { fill: var(--messagebus-stroke); }
+ .t-external { fill: var(--external-stroke); }
+
+ /* Semantic Sigils are small authored role stamps, not icons from a brand
+ pack and not viewer controls. Prefix selectors with svg so the canonical
+ export's SVG-only stylesheet collector keeps them automatically. */
+ svg .semantic-sigil {
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.35;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ opacity: 0.76;
+ pointer-events: none;
+ }
+ svg .semantic-sigil > * { vector-effect: non-scaling-stroke; }
+ svg .semantic-sigil .sigil-fill { fill: currentColor; stroke: none; }
+ svg .s-frontend { color: var(--frontend-stroke); }
+ svg .s-backend { color: var(--backend-stroke); }
+ svg .s-database { color: var(--database-stroke); }
+ svg .s-cloud { color: var(--cloud-stroke); }
+ svg .s-security { color: var(--security-stroke); }
+ svg .s-messagebus { color: var(--messagebus-stroke); }
+ svg .s-external { color: var(--external-stroke); }
+
+ /* Reading Depth is a viewer-only level-of-detail layer. MAP preserves the
+ authored structure and primary labels, READ restores relationship labels
+ and node context, and FULL restores tags and fine annotations. Semantic
+ intent can reveal the relevant detail without moving any geometry. */
+ svg [data-detail] { transition: opacity 160ms ease; }
+ svg [data-detail-anchor] {
+ transform-box: fill-box;
+ transform-origin: center;
+ transition: transform 160ms ease;
+ }
+ .diagram-container[data-detail-level="map"] svg [data-detail="context"],
+ .diagram-container[data-detail-level="map"] svg [data-detail="fine"],
+ .diagram-container[data-detail-level="read"] svg [data-detail="fine"] {
+ opacity: 0;
+ pointer-events: none;
+ }
+ .diagram-container[data-detail-level="map"] svg [data-detail-anchor] {
+ transform: translateY(8px);
+ }
+
+ /* Focus, lens, preview, route, and story states are stronger reader intent than
+ the global zoom level. Reveal only their exact semantic matches. */
+ svg[data-focus-active] [data-focus-match][data-detail],
+ svg[data-focus-active] [data-focus-match] [data-detail],
+ svg[data-reach-active] [data-reach-match][data-detail],
+ svg[data-reach-active] [data-reach-match] [data-detail],
+ svg[data-lens-active] [data-lens-match][data-detail],
+ svg[data-lens-active] [data-lens-match] [data-detail],
+ svg[data-legend-preview-active] [data-legend-preview-match][data-detail],
+ svg[data-legend-preview-active] [data-legend-preview-match] [data-detail],
+ svg[data-intent-trace-active] [data-intent-trace-match][data-detail],
+ svg[data-intent-trace-active] [data-intent-trace-match] [data-detail],
+ svg[data-route-active] [data-route-match][data-detail],
+ svg[data-route-active] [data-route-match] [data-detail],
+ svg[data-story-active] [data-story-step][data-detail],
+ svg[data-story-active] [data-story-step] [data-detail],
+ svg[data-relationship-preview-active] [data-relationship-preview][data-detail],
+ svg[data-relationship-preview-active] [data-relationship-preview] [data-detail],
+ svg [data-node-id]:hover [data-detail],
+ svg [data-node-id]:focus-visible [data-detail] {
+ opacity: 1;
+ pointer-events: auto;
+ }
+ svg[data-focus-active] [data-focus-match] [data-detail-anchor],
+ svg[data-reach-active] [data-reach-match] [data-detail-anchor],
+ svg[data-lens-active] [data-lens-match] [data-detail-anchor],
+ svg[data-legend-preview-active] [data-legend-preview-match] [data-detail-anchor],
+ svg[data-intent-trace-active] [data-intent-trace-match] [data-detail-anchor],
+ svg[data-route-active] [data-route-match] [data-detail-anchor],
+ svg[data-story-active] [data-story-step] [data-detail-anchor],
+ svg [data-node-id]:hover [data-detail-anchor],
+ svg [data-node-id]:focus-visible [data-detail-anchor] { transform: none; }
+
+ /* Arrows */
+ .a-default { stroke: var(--arrow); fill: none; }
+ .a-emphasis { stroke: var(--arrow-emphasis); fill: none; }
+ .a-security { stroke: var(--security-stroke); fill: none; stroke-dasharray: 5,5; }
+ .a-dashed { stroke: var(--database-stroke); fill: none; stroke-dasharray: 4,4; }
+
+ /* Arrowhead markers reference these */
+ .m-default { fill: var(--arrow); }
+ .m-emphasis { fill: var(--arrow-emphasis); }
+ .m-security { fill: var(--security-stroke); }
+ .m-dashed { fill: var(--database-stroke); }
+
+ /* Stable semantic exploration hooks emitted by every renderer. */
+ svg [data-node-id] {
+ cursor: pointer;
+ transition: opacity 0.18s ease, filter 0.18s ease;
+ }
+ svg [data-edge-from] { transition: opacity 0.18s ease, filter 0.18s ease; }
+ svg [data-node-id]:hover,
+ svg [data-node-id]:focus-visible {
+ filter: drop-shadow(0 0 7px var(--frontend-stroke));
+ outline: none;
+ }
+ /* Verified Source Beacons are installed at runtime only for nodes whose
+ repository evidence passed the local revision/blob/line checks. They
+ live inside the owning node so every focus, route, story, and guided
+ view keeps one coherent opacity state. Export serialization removes
+ them before producing the canonical SVG or any derived visual. */
+ .source-evidence-beacon {
+ pointer-events: none;
+ opacity: 0.76;
+ transition: opacity 160ms ease, filter 160ms ease;
+ }
+ .source-evidence-beacon rect {
+ fill: color-mix(in srgb, var(--mask) 92%, var(--backend-stroke));
+ stroke: color-mix(in srgb, var(--backend-stroke) 82%, var(--panel-border));
+ stroke-width: 1;
+ vector-effect: non-scaling-stroke;
+ }
+ .source-evidence-beacon text {
+ fill: var(--backend-stroke);
+ font-size: 5.5px;
+ font-weight: 800;
+ letter-spacing: 0.06em;
+ text-anchor: middle;
+ text-transform: uppercase;
+ }
+ svg [data-node-id]:hover .source-evidence-beacon,
+ svg [data-node-id]:focus-visible .source-evidence-beacon,
+ svg [data-node-id][data-focus-selected] .source-evidence-beacon {
+ opacity: 1;
+ filter: drop-shadow(0 0 4px color-mix(in srgb, var(--backend-stroke) 72%, transparent));
+ }
+ svg[data-preset="signal-flow"] .source-evidence-beacon { opacity: 0.9; }
+ svg[data-preset="blueprint"] .source-evidence-beacon rect {
+ rx: 0.8px;
+ filter: none;
+ }
+ svg [data-legend-kind][role="button"] { cursor: pointer; }
+ svg [data-legend-kind]:focus { outline: none; }
+ svg [data-legend-hit] {
+ fill: transparent;
+ stroke: transparent;
+ stroke-width: 1.4;
+ pointer-events: all;
+ vector-effect: non-scaling-stroke;
+ transition: stroke 150ms ease, stroke-width 150ms ease, filter 150ms ease;
+ }
+ svg [data-legend-count-badge] { pointer-events: none; }
+ svg [data-legend-count-badge] rect {
+ fill: var(--mask);
+ stroke: var(--panel-border);
+ stroke-width: 1;
+ vector-effect: non-scaling-stroke;
+ }
+ svg [data-legend-count-badge] text {
+ fill: var(--text-muted);
+ font-size: 8px;
+ font-weight: 700;
+ text-anchor: middle;
+ }
+ svg [data-legend-kind][role="button"]:hover [data-legend-hit] {
+ stroke: color-mix(in srgb, var(--database-stroke) 58%, transparent);
+ }
+ svg [data-legend-kind][role="button"]:focus-visible [data-legend-hit] {
+ stroke: var(--frontend-stroke);
+ stroke-width: 1.8;
+ filter: drop-shadow(0 0 3px var(--frontend-stroke));
+ }
+ svg [data-legend-kind][data-legend-selected] [data-legend-hit] {
+ stroke: var(--database-stroke);
+ stroke-width: 2.2;
+ stroke-dasharray: 3 2;
+ }
+ svg [data-legend-kind][data-legend-selected] [data-legend-count-badge] rect {
+ stroke: var(--database-stroke);
+ stroke-width: 1.8;
+ }
+ svg [data-legend-kind][data-legend-zero] { opacity: 0.44; }
+ svg[data-preset="signal-flow"] [data-legend-kind][data-legend-selected] [data-legend-hit] {
+ filter: drop-shadow(0 0 4px var(--database-stroke));
+ }
+ svg[data-preset="blueprint"] [data-legend-hit],
+ svg[data-preset="blueprint"] [data-legend-count-badge] rect { rx: 0; filter: none; }
+ svg[data-legend-preview-active] [data-node-id],
+ svg[data-legend-preview-active] [data-edge-from] { opacity: 0.11; }
+ svg[data-legend-preview-active] [data-legend-preview-match] { opacity: 1; }
+ svg[data-legend-preview-active] [data-legend-preview-peer] { opacity: 0.62; }
+ svg[data-legend-preview-active] [data-legend-preview-selected] {
+ filter: drop-shadow(0 0 8px var(--database-stroke));
+ }
+ svg[data-lens-active] [data-node-id],
+ svg[data-lens-active] [data-edge-from] { opacity: 0.11; }
+ svg[data-lens-active] [data-lens-match] { opacity: 1; }
+ svg[data-lens-active] [data-lens-peer] { opacity: 0.62; }
+ svg[data-lens-active] [data-lens-selected] {
+ filter: drop-shadow(0 0 10px var(--database-stroke));
+ }
+ .semantic-lens-overlay { pointer-events: none; }
+ .semantic-lens-flow {
+ fill: none;
+ stroke-width: 3.05;
+ stroke-linecap: round;
+ stroke-dasharray: 0.075 0.925;
+ stroke-dashoffset: 0;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.9;
+ animation: archify-semantic-lens-flow 1.35s linear 1 both;
+ animation-delay: var(--lens-flow-delay, 0s);
+ }
+ .semantic-lens-flow[data-direction="out"],
+ .semantic-lens-flow[data-direction="forward"] {
+ stroke: var(--frontend-stroke);
+ filter: drop-shadow(0 0 3px var(--frontend-stroke));
+ }
+ .semantic-lens-flow[data-direction="in"],
+ .semantic-lens-flow[data-direction="reverse"] {
+ stroke: var(--database-stroke);
+ filter: drop-shadow(0 0 3px var(--database-stroke));
+ }
+ .semantic-lens-flow[data-direction="within"] {
+ stroke: var(--messagebus-stroke);
+ filter: drop-shadow(0 0 3px var(--messagebus-stroke));
+ }
+ svg[data-preset="signal-flow"] .semantic-lens-flow {
+ stroke-width: 3.7;
+ opacity: 1;
+ }
+ svg[data-preset="signal-flow"] .semantic-lens-flow[data-direction="out"],
+ svg[data-preset="signal-flow"] .semantic-lens-flow[data-direction="forward"] {
+ filter: drop-shadow(0 0 6px var(--frontend-stroke));
+ }
+ svg[data-preset="signal-flow"] .semantic-lens-flow[data-direction="in"],
+ svg[data-preset="signal-flow"] .semantic-lens-flow[data-direction="reverse"] {
+ filter: drop-shadow(0 0 6px var(--database-stroke));
+ }
+ svg[data-preset="signal-flow"] .semantic-lens-flow[data-direction="within"] {
+ filter: drop-shadow(0 0 6px var(--messagebus-stroke));
+ }
+ svg[data-preset="blueprint"] .semantic-lens-flow {
+ stroke-width: 2.45;
+ stroke-linecap: square;
+ filter: none;
+ opacity: 0.78;
+ }
+ html[data-embed="true"] .semantic-lens-overlay { display: none; }
+ svg[data-focus-active] [data-node-id],
+ svg[data-focus-active] [data-edge-from] { opacity: 0.13; }
+ svg[data-focus-active] [data-focus-match] { opacity: 1; }
+ svg[data-focus-active] [data-focus-selected] {
+ filter: drop-shadow(0 0 10px var(--frontend-stroke));
+ }
+
+ /* Authored Reachability is a bounded graph query over the relationships
+ already present in the artifact. Upstream walks incoming edges;
+ downstream walks outgoing edges. It never claims runtime causality and
+ changes only viewer emphasis, not canonical geometry or export bytes. */
+ svg[data-reach-active] [data-node-id],
+ svg[data-reach-active] [data-edge-from] { opacity: 0.09; }
+ svg[data-reach-active] [data-reach-match] { opacity: 1; }
+ svg[data-reach-active="upstream"] [data-reach-origin] {
+ filter: drop-shadow(0 0 11px var(--database-stroke));
+ }
+ svg[data-reach-active="downstream"] [data-reach-origin] {
+ filter: drop-shadow(0 0 11px var(--backend-stroke));
+ }
+ svg[data-reach-active="upstream"] [data-edge-from][data-reach-match] {
+ filter: drop-shadow(0 0 4px color-mix(in srgb, var(--database-stroke) 72%, transparent));
+ }
+ svg[data-reach-active="downstream"] [data-edge-from][data-reach-match] {
+ filter: drop-shadow(0 0 4px color-mix(in srgb, var(--backend-stroke) 72%, transparent));
+ }
+ svg[data-preset="blueprint"][data-reach-active] [data-reach-origin],
+ svg[data-preset="blueprint"][data-reach-active] [data-edge-from][data-reach-match] {
+ filter: none;
+ }
+
+ /* Direct Relationship Explorer makes authored paths a real input surface
+ without changing their visible stroke or canonical SVG. A transparent
+ runtime rail supplies forgiving pointer geometry; the exact authored
+ path and endpoints continue to carry every visible state. */
+ .relationship-hit-overlay { pointer-events: none; }
+ .relationship-hit-target { outline: none; }
+ .relationship-hit-rail {
+ fill: none;
+ stroke: transparent;
+ stroke-width: 24;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ vector-effect: non-scaling-stroke;
+ pointer-events: stroke;
+ cursor: pointer;
+ }
+ .relationship-focus-rail {
+ fill: none;
+ stroke: color-mix(in srgb, var(--frontend-stroke) 34%, transparent);
+ stroke-width: 6;
+ stroke-dasharray: 2 4;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ vector-effect: non-scaling-stroke;
+ pointer-events: none;
+ opacity: 0;
+ }
+ .relationship-hit-target:focus-visible .relationship-focus-rail {
+ opacity: 1;
+ }
+ svg[data-preset="blueprint"] .relationship-hit-target:focus-visible .relationship-focus-rail {
+ stroke-linecap: square;
+ filter: none;
+ }
+ svg[data-relationship-direct-active] [data-node-id],
+ svg[data-relationship-direct-active] [data-edge-from] { opacity: 0.16; }
+ svg[data-relationship-direct-active] [data-relationship-preview],
+ svg[data-relationship-direct-active] [data-relationship-preview-node] { opacity: 1; }
+ svg[data-focus-active]:not([data-relationship-pin-active]) .relationship-hit-rail,
+ svg[data-story-active] .relationship-hit-rail,
+ svg[data-route-picking] .relationship-hit-rail,
+ svg[data-route-active] .relationship-hit-rail,
+ svg[data-lens-active] .relationship-hit-rail,
+ svg[data-chapter-preview] .relationship-hit-rail { pointer-events: none; }
+ html[data-embed="true"] .relationship-hit-overlay { display: none; }
+ @media (hover: none), (pointer: coarse) {
+ .relationship-hit-rail { stroke-width: 24; }
+ }
+
+ /* Relationship Preview is temporary exploration state layered on top of
+ neighborhood focus. It changes emphasis only; exported geometry and the
+ relationship's original solid/dashed visual language stay untouched. */
+ svg[data-relationship-preview-active] [data-focus-match] { opacity: 0.18; }
+ svg[data-relationship-preview-active] [data-relationship-preview],
+ svg[data-relationship-preview-active] [data-relationship-preview-node] { opacity: 1; }
+ svg[data-relationship-preview-active] [data-relationship-preview] {
+ filter: drop-shadow(0 0 5px var(--arrow-emphasis));
+ }
+ svg[data-relationship-preview-active] [data-relationship-preview] path,
+ svg[data-relationship-preview-active] path[data-relationship-preview],
+ svg[data-relationship-preview-active] [data-relationship-preview] line,
+ svg[data-relationship-preview-active] line[data-relationship-preview],
+ svg[data-relationship-preview-active] [data-relationship-preview] polyline,
+ svg[data-relationship-preview-active] polyline[data-relationship-preview] {
+ stroke-width: 2.75;
+ }
+ svg[data-relationship-preview-active] [data-relationship-preview-source] {
+ filter: drop-shadow(0 0 7px var(--frontend-stroke));
+ }
+ svg[data-relationship-preview-active] [data-relationship-preview-target] {
+ filter: drop-shadow(0 0 7px var(--database-stroke));
+ }
+ .relationship-pulse-overlay { pointer-events: none; }
+ .relationship-flow-pulse {
+ fill: none;
+ stroke: var(--arrow-emphasis);
+ stroke-width: 3.35;
+ stroke-linecap: round;
+ stroke-dasharray: 0.085 0.915;
+ stroke-dashoffset: 0;
+ vector-effect: non-scaling-stroke;
+ opacity: 0;
+ filter: drop-shadow(0 0 4px var(--arrow-emphasis));
+ animation: archify-relationship-pulse 1.2s linear 1 both;
+ }
+ svg[data-preset="signal-flow"] .relationship-flow-pulse {
+ stroke: var(--frontend-stroke);
+ stroke-width: 3.85;
+ filter: drop-shadow(0 0 7px var(--frontend-stroke));
+ }
+ svg[data-preset="blueprint"] .relationship-flow-pulse {
+ stroke-width: 2.5;
+ stroke-linecap: square;
+ filter: none;
+ }
+ svg[data-preset="editorial"] .relationship-flow-pulse {
+ stroke: var(--arrow-emphasis);
+ stroke-width: 2.75;
+ filter: none;
+ }
+ .semantic-flow-token {
+ color: var(--frontend-stroke);
+ opacity: 0;
+ pointer-events: none;
+ }
+ .relationship-flow-token {
+ animation: archify-relationship-token-life 1.2s linear 1 both;
+ }
+ .story-flow-token {
+ animation: archify-relationship-token-life 0.78s linear 1 both;
+ }
+ .semantic-flow-token-halo {
+ fill: var(--mask);
+ stroke: currentColor;
+ stroke-width: 1.1;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.96;
+ }
+ .relationship-flow-token-shape {
+ fill: var(--mask);
+ stroke: currentColor;
+ stroke-width: 1.35;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ vector-effect: non-scaling-stroke;
+ }
+ .relationship-flow-token-ink {
+ fill: none;
+ stroke: currentColor;
+ stroke-width: 1.05;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+ }
+ .relationship-flow-token-dot { fill: currentColor; stroke: none; }
+ .semantic-flow-token[data-token-kind="data"] { color: var(--database-stroke); }
+ .semantic-flow-token[data-token-kind="event"] { color: var(--messagebus-stroke); }
+ .semantic-flow-token[data-token-kind="security"] { color: var(--security-stroke); }
+ .semantic-flow-token[data-token-kind="state"] { color: var(--cloud-stroke); }
+ svg[data-preset="signal-flow"] .semantic-flow-token {
+ filter: drop-shadow(0 0 5px currentColor);
+ }
+ svg[data-preset="blueprint"] .semantic-flow-token { filter: none; }
+ svg[data-preset="editorial"] .semantic-flow-token { filter: none; }
+ html[data-embed="true"] .relationship-pulse-overlay { display: none; }
+
+ /* Intent Trace is a temporary, pre-click exploration layer. Related
+ topology stays readable while a short viewer-only signal runs over
+ cloned edge geometry; the authored edge remains untouched underneath. */
+ svg[data-intent-trace-active] [data-node-id],
+ svg[data-intent-trace-active] [data-edge-from] { opacity: 0.2; }
+ svg[data-intent-trace-active] [data-intent-trace-match] { opacity: 1; }
+ svg[data-intent-trace-active] [data-intent-trace-selected] {
+ filter: drop-shadow(0 0 10px var(--frontend-stroke));
+ }
+ .intent-trace-overlay { pointer-events: none; }
+ .intent-trace-flow {
+ fill: none;
+ stroke-width: 3.1;
+ stroke-linecap: round;
+ stroke-dasharray: 0.1 0.9;
+ stroke-dashoffset: 0;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.94;
+ animation: archify-intent-trace-flow 1.15s linear 1 both;
+ }
+ .intent-trace-flow[data-direction="out"] {
+ stroke: var(--frontend-stroke);
+ filter: drop-shadow(0 0 4px var(--frontend-stroke));
+ }
+ .intent-trace-flow[data-direction="in"] {
+ stroke: var(--database-stroke);
+ filter: drop-shadow(0 0 4px var(--database-stroke));
+ animation-direction: reverse;
+ }
+ .intent-trace-flow[data-direction="loop"] {
+ stroke: var(--security-stroke);
+ filter: drop-shadow(0 0 4px var(--security-stroke));
+ }
+ svg[data-preset="blueprint"] [data-intent-trace-selected],
+ svg[data-preset="blueprint"] .intent-trace-flow {
+ filter: none;
+ stroke-linecap: square;
+ }
+ svg[data-preset="editorial"] [data-intent-trace-selected],
+ svg[data-preset="editorial"] .intent-trace-flow { filter: none; }
+ .intent-trace-status {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ clip-path: inset(50%);
+ white-space: nowrap;
+ border: 0;
+ }
+
+ /* Route Probe separates a durable two-point query from selection and
+ one-hop hover. Only the chosen shortest directed path stays prominent;
+ the traveling signal is cloned viewer geometry and never authored SVG. */
+ svg[data-route-picking="target"] [data-node-id] { opacity: 0.24; }
+ svg[data-route-picking="target"] [data-route-candidate],
+ svg[data-route-picking="target"] [data-route-start] { opacity: 1; }
+ svg[data-route-picking] [data-node-id] { cursor: crosshair; }
+ svg[data-route-picking] [data-route-start] {
+ filter: drop-shadow(0 0 10px var(--frontend-stroke));
+ }
+ svg[data-route-active] [data-node-id],
+ svg[data-route-active] [data-edge-from] { opacity: 0.11; }
+ svg[data-route-active] [data-route-match] { opacity: 1; }
+ svg[data-route-active] [data-route-start] {
+ filter: drop-shadow(0 0 11px var(--frontend-stroke));
+ }
+ svg[data-route-active] [data-route-end] {
+ filter: drop-shadow(0 0 11px var(--security-stroke));
+ }
+ svg[data-route-active] [data-route-step]:not([data-route-start]):not([data-route-end]) {
+ filter: drop-shadow(0 0 7px var(--backend-stroke));
+ }
+ svg[data-route-journey] [data-route-match][data-route-journey-state="past"] { opacity: 0.62; }
+ svg[data-route-journey] [data-route-match][data-route-journey-state="future"] { opacity: 0.34; }
+ svg[data-route-journey] [data-route-match][data-route-journey-state="current"] { opacity: 1; }
+ svg[data-route-journey] [data-node-id][data-route-journey-current] {
+ filter: drop-shadow(0 0 10px var(--backend-stroke));
+ }
+ svg[data-route-journey] [data-edge-from][data-route-journey-current] {
+ opacity: 1;
+ filter: drop-shadow(0 0 6px var(--backend-stroke));
+ }
+ .route-probe-overlay { pointer-events: none; }
+ .route-journey-overlay { pointer-events: none; }
+ .route-probe-flow {
+ fill: none;
+ stroke: var(--backend-stroke);
+ stroke-width: 3.25;
+ stroke-linecap: round;
+ stroke-dasharray: 0.085 0.915;
+ stroke-dashoffset: 0;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.96;
+ filter: drop-shadow(0 0 5px var(--backend-stroke));
+ animation: archify-route-probe-flow 1.1s cubic-bezier(0.22, 1, 0.36, 1) 1 both;
+ animation-delay: calc(var(--route-step, 0) * 0.16s);
+ }
+ .route-journey-flow {
+ fill: none;
+ stroke: var(--backend-stroke);
+ stroke-width: 3.5;
+ stroke-linecap: round;
+ stroke-dasharray: 0.1 0.9;
+ stroke-dashoffset: 0;
+ vector-effect: non-scaling-stroke;
+ opacity: 0;
+ filter: drop-shadow(0 0 6px var(--backend-stroke));
+ animation: archify-route-journey-flow 780ms cubic-bezier(0.22, 1, 0.36, 1) 1 both;
+ }
+ svg[data-preset="blueprint"] [data-route-start],
+ svg[data-preset="blueprint"] [data-route-end],
+ svg[data-preset="blueprint"] [data-route-step],
+ svg[data-preset="blueprint"] .route-probe-flow {
+ filter: none;
+ stroke-linecap: square;
+ }
+ svg[data-preset="blueprint"] .route-journey-flow {
+ filter: none;
+ stroke-linecap: square;
+ }
+ svg[data-preset="editorial"] [data-route-start],
+ svg[data-preset="editorial"] [data-route-end],
+ svg[data-preset="editorial"] [data-route-step],
+ svg[data-preset="editorial"] .route-probe-flow,
+ svg[data-preset="editorial"] .route-journey-flow { filter: none; }
+
+ svg[data-preset="editorial"] .c-grid {
+ stroke-dasharray: 1 4;
+ opacity: 0.48;
+ }
+ svg[data-preset="editorial"] .c-region {
+ fill: color-mix(in srgb, var(--cloud-fill) 48%, transparent);
+ stroke-dasharray: 9 4;
+ }
+ svg[data-preset="editorial"] [data-node-id]:hover,
+ svg[data-preset="editorial"] [data-node-id]:focus-visible,
+ svg[data-preset="editorial"][data-focus-active] [data-focus-selected] {
+ filter: drop-shadow(0 2px 2px color-mix(in srgb, var(--text) 18%, transparent));
+ }
+
+ svg[data-preset="blueprint"] .c-grid {
+ stroke-dasharray: 1 3;
+ opacity: 0.78;
+ }
+ svg[data-preset="blueprint"] .c-region {
+ fill: color-mix(in srgb, var(--cloud-fill) 58%, transparent);
+ stroke-dasharray: 12 4 2 4;
+ }
+ svg[data-preset="blueprint"] .c-security-group,
+ svg[data-preset="blueprint"] .c-lane { stroke-dasharray: 7 3; }
+ svg[data-preset="blueprint"] [data-node-id]:hover,
+ svg[data-preset="blueprint"] [data-node-id]:focus-visible {
+ filter: drop-shadow(0 0 3px var(--frontend-stroke));
+ }
+ svg[data-preset="blueprint"][data-focus-active] [data-focus-selected] {
+ filter: drop-shadow(0 0 3px var(--frontend-stroke));
+ }
+ svg[data-preset="blueprint"][data-focus-active][data-reach-active] [data-focus-selected][data-reach-origin] {
+ filter: none;
+ }
+ svg[data-preset="blueprint"][data-relationship-preview-active] [data-relationship-preview],
+ svg[data-preset="blueprint"][data-relationship-preview-active] [data-relationship-preview-source],
+ svg[data-preset="blueprint"][data-relationship-preview-active] [data-relationship-preview-target] {
+ filter: drop-shadow(0 0 3px var(--frontend-stroke));
+ }
+
+ /* Story Trail is a viewer-only overlay derived from the ordered focus IDs
+ of the active guided view. The authored edge stays underneath with its
+ original color, dash pattern, marker, and geometry intact. */
+ .story-trail-overlay { pointer-events: none; }
+ .story-carrier-overlay { pointer-events: none; }
+ .story-trail-flow {
+ fill: none;
+ stroke: var(--frontend-stroke);
+ stroke-width: 2.8;
+ stroke-linecap: round;
+ vector-effect: non-scaling-stroke;
+ opacity: 0.34;
+ filter: drop-shadow(0 0 2px var(--frontend-stroke));
+ }
+ svg[data-story-beat] [data-story-step] {
+ opacity: 0.22;
+ filter: saturate(0.42);
+ transition: opacity 180ms ease, filter 180ms ease;
+ }
+ svg[data-story-beat] [data-story-step][data-story-beat-state="past"] {
+ opacity: 0.72;
+ filter: saturate(0.82);
+ }
+ svg[data-story-beat] [data-story-step][data-story-beat-state="next"] {
+ opacity: 0.5;
+ filter: saturate(0.66);
+ }
+ svg[data-story-beat] [data-story-step][data-story-beat-state="active"] {
+ opacity: 1;
+ filter: drop-shadow(0 0 7px var(--frontend-stroke));
+ animation: archify-story-beat-node 360ms cubic-bezier(0.22, 1, 0.36, 1) both;
+ }
+ svg[data-story-beat] [data-edge-from][data-story-beat-step] {
+ opacity: 0.16;
+ transition: opacity 160ms ease;
+ }
+ svg[data-story-beat] [data-edge-from][data-story-beat-step][data-story-beat-state="past"] { opacity: 0.58; }
+ svg[data-story-beat] [data-edge-from][data-story-beat-step][data-story-beat-state="next"] { opacity: 0.34; }
+ svg[data-story-beat] [data-edge-from][data-story-beat-step][data-story-beat-state="active"] { opacity: 1; }
+ svg[data-story-beat] .story-trail-flow { opacity: 0; transition: opacity 160ms ease; }
+ svg[data-story-beat] .story-trail-flow[data-story-beat-state="past"] { opacity: 0.34; }
+ svg[data-story-beat] .story-trail-flow[data-story-beat-state="next"] { opacity: 0.2; }
+ svg[data-story-beat] .story-trail-flow[data-story-beat-state="active"] { opacity: 0.92; }
+ svg[data-story-beat] .story-trail-flow[data-story-pulse="true"] {
+ stroke-dasharray: 2 13;
+ stroke-dashoffset: 30;
+ opacity: 0.92;
+ animation: archify-story-flow 0.78s linear 1 both;
+ }
+ svg[data-preset="blueprint"] .story-trail-flow {
+ stroke-linecap: square;
+ filter: none;
+ opacity: 0.5;
+ }
+ svg[data-preset="blueprint"][data-story-beat] [data-story-step][data-story-beat-state="active"] {
+ filter: none;
+ animation: none;
+ }
+ svg[data-preset="editorial"] .story-trail-flow { filter: none; }
+ svg[data-preset="editorial"][data-story-beat] [data-story-step][data-story-beat-state="active"] {
+ filter: drop-shadow(0 2px 2px color-mix(in srgb, var(--text) 16%, transparent));
+ }
+
+ /* Chapter Delta Preview is a static, pre-commit comparison between two
+ chapter focus sets. Symbols in the rail carry the meaning without color;
+ these SVG rules only make exact stable-ID membership easier to scan. */
+ svg[data-chapter-preview] [data-node-id],
+ svg[data-chapter-preview] [data-edge-from] {
+ opacity: 0.1 !important;
+ filter: none !important;
+ transition: none !important;
+ }
+ svg[data-chapter-preview] .story-trail-overlay { opacity: 0.08 !important; }
+ svg[data-chapter-preview] [data-node-id][data-chapter-preview-role="stay"] {
+ opacity: 0.76 !important;
+ filter: saturate(0.7) !important;
+ }
+ svg[data-chapter-preview] [data-node-id][data-chapter-preview-role="enter"] {
+ opacity: 1 !important;
+ filter: saturate(1.2) !important;
+ }
+ svg[data-chapter-preview] [data-node-id][data-chapter-preview-role="leave"] {
+ opacity: 0.28 !important;
+ filter: grayscale(0.72) !important;
+ }
+ svg[data-preset="blueprint"][data-chapter-preview] [data-node-id] { filter: none !important; }
+
+ /* Dashed boundary variants */
+ .c-security-group { fill: transparent; stroke: var(--security-stroke); stroke-dasharray: 4,4; }
+ .c-region { fill: rgba(251, 191, 36, 0.05); stroke: var(--cloud-stroke); stroke-dasharray: 8,4; }
+ .c-lane { fill: var(--lane-fill); stroke: var(--lane-stroke); stroke-dasharray: 6,6; }
+
+ /* Optional trace animation, enabled only by meta.animation = "trace".
+ The ordinary viewer performs one ambient pass and then restores the
+ authored solid/security/async line language. WebM samples the same
+ authored geometry into an explicit canvas timeline. */
+ html[data-ambient-motion="running"] svg[data-animation="trace"] [data-animate="edge"] {
+ animation: archify-edge-flow 2.4s linear 1;
+ animation-delay: calc(var(--step, 0) * 160ms);
+ }
+ html[data-ambient-motion="running"] svg[data-animation="trace"] [data-animate="node"] {
+ transform-box: fill-box;
+ transform-origin: center;
+ animation: archify-node-pulse 3.6s ease-in-out 1;
+ animation-delay: calc(var(--step, 0) * 160ms);
+ }
+ svg[data-preset="signal-flow"][data-animation="trace"] [data-animate="edge"] {
+ stroke-linecap: round;
+ filter: drop-shadow(0 0 3px var(--arrow-emphasis));
+ animation-duration: 1.75s;
+ }
+ svg[data-preset="signal-flow"][data-animation="trace"] [data-animate="node"] {
+ animation-duration: 3.1s;
+ }
+ svg[data-preset="blueprint"][data-animation="trace"] [data-animate="edge"] {
+ stroke-linecap: square;
+ filter: none;
+ animation-duration: 2.15s;
+ }
+ svg[data-preset="blueprint"][data-animation="trace"] [data-animate="node"] {
+ animation-name: archify-blueprint-node-pulse;
+ animation-duration: 3.8s;
+ }
+ svg[data-preset="editorial"][data-animation="trace"] [data-animate="edge"] {
+ filter: none;
+ animation-duration: 2.2s;
+ }
+ svg[data-preset="editorial"][data-animation="trace"] [data-animate="node"] {
+ animation-name: archify-editorial-node-pulse;
+ animation-duration: 3.5s;
+ }
+ /* Embedded proofs are static until a named Story Trail chapter is played.
+ This keeps proof grids calm and ensures ?play=1 stops all viewer motion
+ after one 3.2 second chapter instead of leaving ambient trace loops on. */
+ html[data-embed="true"] svg[data-animation="trace"] [data-animate],
+ html[data-share-playback="true"] svg[data-animation="trace"] [data-animate] {
+ animation: none !important;
+ stroke-dashoffset: 0;
+ }
+ @keyframes archify-edge-flow {
+ 0% { stroke-dasharray: 10 8; stroke-dashoffset: 54; opacity: 0.42; }
+ 88% { stroke-dasharray: 10 8; stroke-dashoffset: 0; opacity: 1; }
+ 99.9% { stroke-dasharray: 10 8; stroke-dashoffset: 0; opacity: 1; }
+ 100% { stroke-dashoffset: 0; opacity: 1; }
+ }
+ @keyframes archify-node-pulse {
+ 0%, 72%, 100% { filter: none; stroke-width: 1.5; }
+ 18%, 36% {
+ filter: drop-shadow(0 0 8px var(--arrow-emphasis));
+ stroke-width: 2.4;
+ }
+ }
+ @keyframes archify-blueprint-node-pulse {
+ 0%, 72%, 100% { opacity: 1; filter: none; }
+ 18%, 36% { opacity: 0.72; filter: drop-shadow(0 0 2px var(--frontend-stroke)); }
+ }
+ @keyframes archify-editorial-node-pulse {
+ 0%, 72%, 100% { opacity: 1; filter: none; }
+ 18%, 36% { opacity: 0.78; filter: drop-shadow(0 2px 2px color-mix(in srgb, var(--text) 18%, transparent)); }
+ }
+ @keyframes archify-signal-scan {
+ 0%, 18% { opacity: 1; transform: translateX(-70%); }
+ 70% { opacity: 1; transform: translateX(70%); }
+ 100% { opacity: 0; transform: translateX(70%); }
+ }
+ @keyframes archify-radar-live {
+ 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--backend-stroke) 52%, transparent); }
+ 55%, 100% { box-shadow: 0 0 0 0.32rem transparent; }
+ }
+ @keyframes archify-guided-progress {
+ from { transform: scaleX(var(--guided-progress-start, 0)); }
+ to { transform: scaleX(1); }
+ }
+ @keyframes archify-story-flow {
+ to { stroke-dashoffset: 0; }
+ }
+ @keyframes archify-intent-trace-flow {
+ 0% { opacity: 0.28; stroke-dashoffset: 0; }
+ 12%, 78% { opacity: 0.94; }
+ 100% { opacity: 0; stroke-dashoffset: -1; }
+ }
+ @keyframes archify-route-probe-flow {
+ 0% { stroke-dashoffset: 0; opacity: 0.96; }
+ 78% { stroke-dashoffset: -1; opacity: 0.96; }
+ 100% { stroke-dasharray: none; stroke-dashoffset: 0; opacity: 0.58; }
+ }
+ @keyframes archify-route-journey-flow {
+ 0% { opacity: 0.16; stroke-dashoffset: 0; }
+ 14%, 76% { opacity: 1; }
+ 100% { opacity: 0; stroke-dashoffset: -1; }
+ }
+ @keyframes archify-semantic-lens-flow {
+ 0% { opacity: 0.2; stroke-dashoffset: 0; }
+ 12%, 78% { opacity: 0.9; }
+ 100% { opacity: 0; stroke-dashoffset: -1; }
+ }
+ @keyframes archify-relationship-pulse {
+ 0% { opacity: 0.18; stroke-dashoffset: 0; }
+ 10%, 76% { opacity: 0.98; }
+ 100% { opacity: 0; stroke-dashoffset: -1; }
+ }
+ @keyframes archify-relationship-token-life {
+ 0%, 7% { opacity: 0; }
+ 13%, 82% { opacity: 1; }
+ 100% { opacity: 0; }
+ }
+ @keyframes archify-story-beat-node {
+ from { opacity: 0.48; filter: saturate(0.6); }
+ 58% { opacity: 1; filter: drop-shadow(0 0 10px var(--frontend-stroke)); }
+ to { opacity: 1; filter: drop-shadow(0 0 7px var(--frontend-stroke)); }
+ }
+ @keyframes archify-share-cue-enter {
+ from { opacity: 0; transform: translate(-50%, -0.45rem); }
+ to { opacity: 1; transform: translate(-50%, 0); }
+ }
+ @keyframes archify-share-cue-pulse {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.48; transform: scale(0.78); }
+ }
+ @media (prefers-reduced-motion: reduce) {
+ html[data-preset="signal-flow"] .diagram-container::before {
+ animation: none !important;
+ }
+ .pulse-dot { animation: none !important; }
+ svg[data-animation="trace"] [data-animate] {
+ animation: none !important;
+ stroke-dashoffset: 0;
+ }
+ .guided-view-progress span {
+ animation: none !important;
+ transform: scaleX(1) !important;
+ }
+ .story-trail-flow,
+ .intent-trace-flow,
+ .route-probe-flow,
+ .semantic-lens,
+ .diagram-guide,
+ [data-story-step],
+ .overview-map-live,
+ .guided-view-stop,
+ .share-chapter-cue,
+ .chapter-handoff-anchor,
+ .share-chapter-state::before,
+ .share-chapter-progress span { animation: none !important; }
+ .guided-view-chapter,
+ svg[data-chapter-preview] [data-node-id],
+ svg[data-chapter-preview] [data-edge-from] { transition: none !important; }
+ svg[data-story-playing="true"] .story-trail-flow {
+ stroke-dasharray: none;
+ stroke-dashoffset: 0;
+ opacity: 0.55;
+ }
+ .intent-trace-flow {
+ animation: none !important;
+ stroke-dasharray: none;
+ stroke-dashoffset: 0;
+ opacity: 0.72;
+ }
+ .route-probe-flow {
+ animation: none !important;
+ stroke-dasharray: none;
+ stroke-dashoffset: 0;
+ opacity: 0.78;
+ }
+ .route-journey-overlay,
+ .story-carrier-overlay { display: none !important; }
+ .semantic-lens-flow {
+ animation: none !important;
+ stroke-dasharray: none;
+ stroke-width: 2.2;
+ opacity: 0.34;
+ filter: none;
+ }
+ .relationship-pulse-overlay { display: none !important; }
+ .guided-view-chapter { transition: none !important; }
+ .source-evidence-beacon,
+ svg [data-node-id], svg [data-edge-from], svg [data-detail], svg [data-detail-anchor], svg [data-legend-hit], svg { transition: none !important; }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Guided views
+
+
+
+
+
Explore this system
+
Step through curated paths without changing the source diagram.
+
+
+ Beat
+
+
+
+ Next
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ready
+ Chapter 01 / 01
+
+
+ Guided chapter
+
+
+
+
+
+
+
+
+ Diagram guide
+ Explore this system
+
+
+
+
Inspecting compiled semantics
+
+
+
+
+
+
+
+
+
+ E ExportT ThemeS Style0 Reset+ Zoom in- Zoom outEsc Close
+
+
+
+
+
+ Find a node
+
+
+
+ ⌕
+
+ /
+
+
+
No matching nodes
+
+
+
+
+
+
Semantic passport
+
+
+
+
+
+
+
+
+
+
+
+
Authored reach
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Route probe
+ Choose a start node
+
+
+
+
+
+
+
+
+ Pick two semantic nodes on the diagram
+
+
+
+
+
+
+
+
Choose the source, then the destination. Direction matters.
+
+
+
+
+ Semantic lens
+ Compare system roles
+
+
+
+
Choose up to two semantic kinds. One reveals its real traffic; two compare only direct authored relationships.
+
+
Choose a kind to inspect its nodes and touching relationships.
+
+
+
+
+
+
+
+
+
+ Semantic radar
+ Building overview
+
+
+
+
+
Click nodeDrag to pan
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - • The main request is Web App -> API -> data source -> response
+ - • Return messages are quieter than forward calls
+ - • Activation bars make ownership duration visible
+
+
+
+
+
+
+ - • JWT verification is colored as a security interaction
+ - • Cache miss is visible without overpowering the main path
+ - • Database access only appears after cache fallback
+
+
+
+
+
+
+ - • Trace emission is dashed and secondary
+ - • It does not block the response path
+ - • The diagram separates user-facing latency from observability
+
+
-
-
-
+
diff --git a/examples/sequence-strict.drawio b/examples/sequence-strict.drawio
new file mode 100644
index 0000000..ac305e9
--- /dev/null
+++ b/examples/sequence-strict.drawio
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/sequence.drawio b/examples/sequence.drawio
new file mode 100644
index 0000000..56431f0
--- /dev/null
+++ b/examples/sequence.drawio
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/web-app-rendered.html b/examples/web-app-rendered.html
index b454244..d3d6579 100644
--- a/examples/web-app-rendered.html
+++ b/examples/web-app-rendered.html
@@ -1,5 +1,5 @@
-
+
@@ -3424,6 +3424,11 @@
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
}
+ .toolbar .export-menu button[data-format="drawio"]::before,
+ .toolbar .export-menu button[data-format="drawio-strict"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
.toolbar .export-menu button[data-format="share-card"],
.toolbar .export-menu button[data-action="route-share-card"],
.toolbar .export-menu button[data-action="reach-share-card"] {
@@ -4825,6 +4830,8 @@
@@ -6602,7 +6609,7 @@
Security
// WebP encoding (older Safari), so detect explicitly.
function supports(format) {
if (format === 'share-card') return true;
- if (format === 'svg' || format === 'png') return true;
+ if (format === 'svg' || format === 'png' || format === 'drawio' || format === 'drawio-strict') return true;
if (format === 'webm') return canRecordMotion();
var mime = format === 'jpeg' ? 'image/jpeg' : 'image/webp';
try {
@@ -6707,6 +6714,698 @@
Security
}
});
+ // ---- draw.io export -------------------------------------------------
+ // Converts the canonical SVG into a draw.io mxGraphModel XML so the
+ // diagram can be opened and further edited in draw.io. Every edge is
+ // bound to its source/target vertex cell (dragging a box keeps its
+ // connections), and interior route waypoints are preserved. Mirrors
+ // the Node-side converter in renderers/shared/svg-to-drawio.mjs.
+ var DRAWIO_KIND_STYLE = {
+ frontend: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
+ backend: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;',
+ database: 'shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#fff2cc;strokeColor=#d6b656;',
+ cloud: 'ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;',
+ security: 'shape=shield;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;',
+ messagebus: 'shape=queue;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;',
+ external: 'shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;'
+ };
+ var DRAWIO_STATE_STYLE = {
+ start: 'ellipse;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;',
+ active: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;',
+ waiting: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;',
+ decision: 'rhombus;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;',
+ success: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;strokeWidth=2;',
+ failure: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;strokeWidth=2;',
+ neutral: 'rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666666;',
+ external: 'shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;'
+ };
+ var DRAWIO_FRAME_STYLE = {
+ region: 'rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#9673a6;dashed=0;verticalAlign=top;fontStyle=1;',
+ 'security-group': 'rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#d79b00;dashed=1;verticalAlign=top;fontStyle=1;',
+ lane: 'rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#b0b0b0;verticalAlign=top;align=left;spacingLeft=8;',
+ 'exception-lane': 'rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#b85450;dashed=1;verticalAlign=top;align=left;spacingLeft=8;',
+ stage: 'rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#b0b0b0;verticalAlign=top;align=center;fontStyle=1;',
+ group: 'rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#6c8ebf;dashed=1;verticalAlign=top;',
+ segment: 'rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#b0b0b0;verticalAlign=top;align=left;spacingLeft=8;'
+ };
+ function drawioEscapeAttr(v) {
+ return String(v == null ? '' : v).replace(/[&"<>']/g, function (ch) {
+ return ch === '&' ? '&' : ch === '"' ? '"' : ch === '<' ? '<' : ch === '>' ? '>' : ''';
+ });
+ }
+ function drawioAttr(tag, name) {
+ var m = tag.match(new RegExp('\\s' + name + '="([^"]*)"'));
+ return m ? m[1] : null;
+ }
+ function drawioParsePoints(value) {
+ if (!value) return [];
+ return value.split(';').filter(Boolean).map(function (pair) {
+ var c = pair.split(',').map(Number);
+ return [c[0], c[1]];
+ });
+ }
+ function drawioFindGroupClose(svg, openIndex) {
+ var depth = 1, i = openIndex;
+ while (depth > 0 && i < svg.length) {
+ var no = svg.indexOf('
', i + 1);
+ if (nc === -1) return -1;
+ if (no !== -1 && no < nc) { depth += 1; i = no; }
+ else { depth -= 1; i = nc; }
+ }
+ return i;
+ }
+ // Parse the self-describing SVG into {viewBox, nodes, edges, boundaries, lifelines}.
+ function drawioParseSvg(svg) {
+ var vb = svg.match(new RegExp('<' + 'svg[^>]*\\bviewBox="0 0 (\\d+\\.?\\d*) (\\d+\\.?\\d*)"'));
+ var viewBox = vb ? [parseFloat(vb[1]), parseFloat(vb[2])] : [1000, 700];
+ // Nodes: with first inner .
+ var nodes = [];
+ var nodeMatches = Array.from(svg.matchAll(/]*\bdata-node-id="([^"]*)"[^>]*>/g));
+ nodeMatches.forEach(function (m) {
+ var open = m[0];
+ var ci = drawioFindGroupClose(svg, m.index);
+ var inner = svg.slice(m.index + open.length, ci);
+ var rm = inner.match(/]*>/);
+ if (!rm) return;
+ var box = rm[0];
+ nodes.push({
+ id: m[1],
+ kind: drawioAttr(open, 'data-node-kind') || 'backend',
+ label: drawioAttr(open, 'data-node-label') || '',
+ sublabel: drawioAttr(open, 'data-node-sublabel'),
+ tag: drawioAttr(open, 'data-node-tag'),
+ x: parseFloat(drawioAttr(box, 'x') || '0'),
+ y: parseFloat(drawioAttr(box, 'y') || '0'),
+ width: parseFloat(drawioAttr(box, 'width') || '0'),
+ height: parseFloat(drawioAttr(box, 'height') || '0')
+ });
+ });
+ // Edges: with data-composition-points.
+ var edges = [];
+ var edgeMatches = Array.from(svg.matchAll(/]*\bdata-composition-points="([^"]*)"[^>]*>/g));
+ edgeMatches.forEach(function (m) {
+ var tag = m[0];
+ var points = drawioParsePoints(m[1]);
+ if (points.length < 2) return;
+ var from = drawioAttr(tag, 'data-edge-from') || drawioAttr(tag, 'data-composition-edge-from');
+ var to = drawioAttr(tag, 'data-edge-to') || drawioAttr(tag, 'data-composition-edge-to');
+ if (!from || !to) return;
+ edges.push({
+ from: from, to: to,
+ label: drawioAttr(tag, 'data-edge-label') || drawioAttr(tag, 'data-composition-edge-label'),
+ id: drawioAttr(tag, 'data-edge-id') || drawioAttr(tag, 'data-composition-edge-id'),
+ points: points
+ });
+ });
+ // Boundaries: .
+ var boundaries = [];
+ var bMatches = Array.from(svg.matchAll(/]*\bdata-graph-role="structural-frame"[^>]*>/g));
+ bMatches.forEach(function (m) {
+ var tag = m[0];
+ var w = parseFloat(drawioAttr(tag, 'width') || '0');
+ var h = parseFloat(drawioAttr(tag, 'height') || '0');
+ if (w <= 0 || h <= 0) return;
+ var after = svg.slice(m.index + tag.length, m.index + tag.length + 300);
+ var tm = after.match(/]*>([^<]*)<\/text>/);
+ boundaries.push({
+ kind: drawioAttr(tag, 'data-composition-frame-kind') || 'boundary',
+ label: tm ? tm[1].trim() : '',
+ x: parseFloat(drawioAttr(tag, 'x') || '0'),
+ y: parseFloat(drawioAttr(tag, 'y') || '0'),
+ width: w, height: h
+ });
+ });
+ // Sequence lifelines: vertical dashed paths.
+ var lifelines = [];
+ var llMatches = Array.from(svg.matchAll(/]*\bd="M\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s+L\s*\1\s+(\d+\.?\d*)"[^>]*stroke-dasharray/g));
+ llMatches.forEach(function (m) {
+ lifelines.push({ x: parseFloat(m[1]), y1: parseFloat(m[2]), y2: parseFloat(m[3]) });
+ });
+ return { viewBox: viewBox, nodes: nodes, edges: edges, boundaries: boundaries, lifelines: lifelines };
+ }
+ function drawioNodeStyle(node) {
+ // Lifecycle states carry their own type enum.
+ var diagramType = document.documentElement.getAttribute('data-diagram-type') || '';
+ if (diagramType === 'lifecycle' && DRAWIO_STATE_STYLE[node.kind]) return DRAWIO_STATE_STYLE[node.kind];
+ return DRAWIO_KIND_STYLE[node.kind] || DRAWIO_KIND_STYLE.backend;
+ }
+ function drawioBuildXml(parsed) {
+ var vw = parsed.viewBox[0], vh = parsed.viewBox[1];
+ // Resolve container parents: smallest boundary containing node center.
+ // Returns parentId + the boundary's origin (draw.io child coords are
+ // RELATIVE to their parent, so we must subtract the parent origin).
+ var parents = {};
+ var sorted = parsed.boundaries.map(function (b, i) {
+ return Object.assign({}, b, { drawioId: 'boundary-' + i });
+ }).sort(function (a, b) { return a.width * a.height - b.width * b.height; });
+ parsed.nodes.forEach(function (n) {
+ var cx = n.x + n.width / 2, cy = n.y + n.height / 2;
+ for (var i = 0; i < sorted.length; i++) {
+ var b = sorted[i];
+ if (cx >= b.x && cx <= b.x + b.width && cy >= b.y && cy <= b.y + b.height) {
+ parents[n.id] = { drawioId: b.drawioId, ox: b.x, oy: b.y }; break;
+ }
+ }
+ });
+ var vertexIds = {};
+ parsed.nodes.forEach(function (n) { vertexIds[n.id] = true; });
+ var lines = [];
+ lines.push('');
+ lines.push('<' + 'mxGraphModel dx="' + Math.round(vw) + '" dy="' + Math.round(vh) + '" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="' + Math.round(vw) + '" pageHeight="' + Math.round(vh) + '" math="0" shadow="0">');
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ // Boundaries first (containers).
+ parsed.boundaries.forEach(function (b, i) {
+ var style = DRAWIO_FRAME_STYLE[b.kind] || DRAWIO_FRAME_STYLE.region;
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ });
+ // Nodes.
+ parsed.nodes.forEach(function (n) {
+ var style = drawioNodeStyle(n);
+ var pinfo = parents[n.id];
+ var parentId = pinfo ? pinfo.drawioId : '1';
+ var relX = n.x - (pinfo ? pinfo.ox : 0);
+ var relY = n.y - (pinfo ? pinfo.oy : 0);
+ var parts = [n.label, n.sublabel, n.tag].filter(Boolean);
+ var htmlVal = parts.length > 1
+ ? '' + parts[0] + '
' + parts.slice(1).join(' · ') + ''
+ : n.label;
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ });
+ // Sequence lifelines.
+ parsed.lifelines.forEach(function (ll) {
+ var id = 'lifeline-' + Math.round(ll.x) + '-' + Math.round(ll.y1);
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ lines.push(' ');
+ });
+ // Edges: real source/target binding + fixed interior waypoints.
+ parsed.edges.forEach(function (edge, i) {
+ if (!vertexIds[edge.from] || !vertexIds[edge.to]) return;
+ var id = edge.id ? 'edge-' + edge.id : 'edge-' + i;
+ var value = edge.label ? drawioEscapeAttr(edge.label) : '';
+ lines.push(' ');
+ lines.push(' ');
+ // Interior waypoints only (draw.io computes endpoints from vertices).
+ if (edge.points.length > 2) {
+ lines.push(' ');
+ for (var p = 1; p < edge.points.length - 1; p++) {
+ lines.push(' ');
+ }
+ lines.push(' ');
+ }
+ lines.push(' ');
+ lines.push(' ');
+ });
+ lines.push(' ');
+ lines.push('');
+ return lines.join('\n');
+ }
+ function archifySvgToDrawio(svgString) {
+ return drawioBuildXml(drawioParseSvg(svgString));
+ }
+
+ // ---- draw.io strict export -----------------------------------------
+ // 1:1 fidelity variant: reads the LIVE SVG computed styles (exact
+ // composited fill/stroke colors, same corner radii — arcSize = 2 ×
+ // radius on edges — same dash patterns) but always force-resolves the
+ // artifact's LIGHT theme, so the exported file never depends on the
+ // reader's current theme or the browser preference behind it.
+ function drawioParseColor(v) {
+ var s = String(v || '').trim();
+ var m = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*(?:,\s*([\d.]+)\s*)?\)$/);
+ if (m) return { r: +m[1], g: +m[2], b: +m[3], a: m[4] === undefined ? 1 : +m[4] };
+ // Theme variables come back as their authored tokens (hex), while
+ // computed fill/stroke values arrive as rgb()/rgba().
+ var h = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
+ if (h) {
+ var hex = h[1].length === 3 ? h[1].split('').map(function (c) { return c + c; }).join('') : h[1];
+ return { r: parseInt(hex.slice(0, 2), 16), g: parseInt(hex.slice(2, 4), 16), b: parseInt(hex.slice(4, 6), 16), a: 1 };
+ }
+ return null;
+ }
+ function drawioHex(c) {
+ var h = function (n) { var s = Math.round(Math.max(0, Math.min(255, n))).toString(16); return s.length < 2 ? '0' + s : s; };
+ return '#' + h(c.r) + h(c.g) + h(c.b);
+ }
+ function drawioBlend(fg, base) {
+ return { r: fg.r * fg.a + base.r * (1 - fg.a), g: fg.g * fg.a + base.g * (1 - fg.a), b: fg.b * fg.a + base.b * (1 - fg.a), a: 1 };
+ }
+ function drawioCssColor(v, base) {
+ var c = drawioParseColor(v);
+ if (!c) return null;
+ return drawioHex(c.a >= 1 ? c : drawioBlend(c, base));
+ }
+ function drawioRadiusFromD(d) {
+ var m = String(d || '').match(/L\s*(-?[\d.]+)\s+(-?[\d.]+)\s+Q\s*(-?[\d.]+)\s+(-?[\d.]+)/);
+ return m ? Math.round(Math.hypot(+m[3] - +m[1], +m[4] - +m[2])) : 0;
+ }
+ // Run `fn` with the artifact forced to its light theme and restore the
+ // reader's theme afterwards. The swap and restore happen inside one
+ // synchronous task, so the page never repaints in between; it only
+ // affects how the style reads below resolve.
+ function withLightTheme(fn) {
+ var root = document.documentElement;
+ var prev = root.getAttribute('data-theme');
+ root.setAttribute('data-theme', 'light');
+ try {
+ return fn();
+ } finally {
+ if (prev === null) root.removeAttribute('data-theme');
+ else root.setAttribute('data-theme', prev);
+ }
+ }
+ function drawioStrictPalette(svgEl) {
+ var cs = getComputedStyle(svgEl);
+ // Fallbacks are the light-theme values: the export is always light.
+ var mask = drawioParseColor(cs.getPropertyValue('--mask').trim()) || { r: 255, g: 255, b: 255, a: 1 };
+ var bg = drawioParseColor(cs.getPropertyValue('--bg').trim()) || { r: 248, g: 250, b: 252, a: 1 };
+ var text = drawioHex(drawioParseColor(cs.getPropertyValue('--text').trim()) || { r: 15, g: 23, b: 42, a: 1 });
+ var muted = drawioHex(drawioParseColor(cs.getPropertyValue('--text-muted').trim()) || { r: 100, g: 116, b: 139, a: 1 });
+ return {
+ mask: drawioHex(mask), maskC: mask, bg: drawioHex(bg), bgC: bg, text: text, textMuted: muted,
+ of: function (el, base) {
+ var s = getComputedStyle(el);
+ var b = base === 'mask' ? mask : bg;
+ var out = {};
+ var f = drawioCssColor(s.fill, b);
+ if (f) out.fill = f;
+ var st = drawioCssColor(s.stroke, b);
+ if (st) out.stroke = st;
+ var dash = s.strokeDasharray;
+ if (dash && dash !== 'none') {
+ var parts = dash.split(/[\s,]+/).map(Number).filter(function (n) { return n > 0; });
+ if (parts.length) out.dash = parts.join(' ');
+ }
+ return out;
+ }
+ };
+ }
+ function buildStrictXmlLight() {
+ var svgEl = document.querySelector('.diagram-container svg');
+ if (!svgEl) throw new Error('No diagram SVG found.');
+ var P = drawioStrictPalette(svgEl);
+ var FONT = 'JetBrains Mono,ui-monospace,Menlo,monospace';
+ var vb = (svgEl.getAttribute('viewBox') || '0 0 1000 700').split(/\s+/);
+ var vw = Math.round(+vb[2]), vh = Math.round(+vb[3]);
+ // -- nodes -----------------------------------------------------------
+ var nodes = [], nodeById = {};
+ svgEl.querySelectorAll('g[id^="node-"]').forEach(function (g) {
+ var fillRect = null;
+ g.querySelectorAll('rect').forEach(function (r) {
+ var cls = r.getAttribute('class') || '';
+ if (!fillRect && cls.indexOf('c-') === 0 && cls !== 'c-mask') fillRect = r;
+ else if (fillRect && cls !== 'c-mask' && +r.getAttribute('width') > +fillRect.getAttribute('width')) fillRect = r;
+ });
+ var geom = fillRect || g.querySelector('rect');
+ if (!geom) return;
+ var n = {
+ id: g.getAttribute('data-node-id'),
+ label: g.getAttribute('data-node-label') || '',
+ sublabel: g.getAttribute('data-node-sublabel'),
+ tag: g.getAttribute('data-node-tag'),
+ x: +geom.x.baseVal.value, y: +geom.y.baseVal.value,
+ width: +geom.width.baseVal.value, height: +geom.height.baseVal.value,
+ rx: +(fillRect ? fillRect.getAttribute('rx') : 0) || 0,
+ sw: +(fillRect ? fillRect.getAttribute('stroke-width') : 1.5) || 1.5,
+ fillRect: fillRect || geom
+ };
+ nodes.push(n); nodeById[n.id] = n;
+ });
+ // -- boundaries ------------------------------------------------------
+ var boundaries = [];
+ svgEl.querySelectorAll('rect[data-graph-role="structural-frame"]').forEach(function (r) {
+ boundaries.push({
+ kind: r.getAttribute('data-composition-frame-kind') || 'boundary',
+ x: +r.x.baseVal.value, y: +r.y.baseVal.value,
+ width: +r.width.baseVal.value, height: +r.height.baseVal.value,
+ rx: +r.getAttribute('rx') || 0, sw: +r.getAttribute('stroke-width') || 1,
+ el: r, textEl: null
+ });
+ });
+ // Boundary labels: the first sibling following each frame rect.
+ boundaries.forEach(function (b) {
+ var t = b.el.nextElementSibling;
+ if (t && t.tagName.toLowerCase() === 'text') b.textEl = t;
+ });
+ // -- edges -----------------------------------------------------------
+ var edges = [];
+ svgEl.querySelectorAll('path[data-composition-points]').forEach(function (p) {
+ var from = p.getAttribute('data-edge-from') || p.getAttribute('data-composition-edge-from');
+ var to = p.getAttribute('data-edge-to') || p.getAttribute('data-composition-edge-to');
+ if (!from || !to) return;
+ var pts = (p.getAttribute('data-composition-points') || '').split(';').filter(Boolean).map(function (pair) {
+ var c = pair.split(',');
+ return [+c[0], +c[1]];
+ });
+ if (pts.length < 2) return;
+ edges.push({
+ from: from, to: to,
+ label: p.getAttribute('data-edge-label') || p.getAttribute('data-composition-edge-label'),
+ el: p, pts: pts,
+ sw: +p.getAttribute('stroke-width') || 1.5,
+ radius: drawioRadiusFromD(p.getAttribute('d'))
+ });
+ });
+ // -- edge labels (positioned in data-detail groups) -----------
+ var edgeLabels = [];
+ svgEl.querySelectorAll('g[data-detail="context"][data-edge-from]').forEach(function (g) {
+ var t = g.querySelector('text');
+ if (!t) return;
+ edgeLabels.push({
+ from: g.getAttribute('data-edge-from'), to: g.getAttribute('data-edge-to'),
+ x: +t.getAttribute('x'), y: +t.getAttribute('y') - 4, el: t
+ });
+ });
+ // -- lifelines -------------------------------------------------------
+ var lifelines = [];
+ svgEl.querySelectorAll('path').forEach(function (p) {
+ if (p.hasAttribute('data-composition-points')) return;
+ var d = p.getAttribute('d') || '';
+ var m = d.match(/^M\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s+L\s*\1\s+(\d+\.?\d*)$/);
+ if (!m) return;
+ if (getComputedStyle(p).strokeDasharray === 'none') return;
+ lifelines.push({ x: +m[1], y1: +m[2], y2: +m[3], el: p, sw: +p.getAttribute('stroke-width') || 0.8 });
+ });
+ // -- helpers ---------------------------------------------------------
+ function parents() {
+ var sorted = boundaries.map(function (b, i) { return Object.assign({}, b, { id: 'boundary-' + i }); })
+ .sort(function (a, b) { return a.width * a.height - b.width * b.height; });
+ var map = {};
+ nodes.forEach(function (n) {
+ var cx = n.x + n.width / 2, cy = n.y + n.height / 2;
+ for (var i = 0; i < sorted.length; i++) {
+ var b = sorted[i];
+ if (cx >= b.x && cx <= b.x + b.width && cy >= b.y && cy <= b.y + b.height) { map[n.id] = b; break; }
+ }
+ });
+ return map;
+ }
+ var pm = parents();
+ function fractionAlong(pts, target) {
+ var total = 0, segs = [];
+ for (var i = 0; i < pts.length - 1; i++) {
+ var len = Math.hypot(pts[i + 1][0] - pts[i][0], pts[i + 1][1] - pts[i][1]) || 1;
+ segs.push({ ax: pts[i][0], ay: pts[i][1], dx: pts[i + 1][0] - pts[i][0], dy: pts[i + 1][1] - pts[i][1], len: len, start: total });
+ total += len;
+ }
+ var best = 1e30, result = 0.5;
+ segs.forEach(function (s) {
+ var t = Math.max(0, Math.min(1, ((target[0] - s.ax) * s.dx + (target[1] - s.ay) * s.dy) / (s.len * s.len)));
+ var dist = Math.hypot(target[0] - (s.ax + t * s.dx), target[1] - (s.ay + t * s.dy));
+ if (dist < best) { best = dist; result = (s.start + t * s.len) / (total || 1); }
+ });
+ return Math.max(0, Math.min(1, result));
+ }
+ function borderFrac(pt, node) {
+ if (!node) return null;
+ var f = function (v) { return v < 0.04 ? 0 : v > 0.96 ? 1 : Math.round(v * 100) / 100; };
+ return [f((pt[0] - node.x) / node.width), f((pt[1] - node.y) / node.height)];
+ }
+ // -- emit ------------------------------------------------------------
+ var L = [];
+ L.push('');
+ L.push('<' + 'mxGraphModel dx="' + vw + '" dy="' + vh + '" grid="0" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="' + vw + '" pageHeight="' + vh + '" math="0" shadow="0" background="' + P.bg + '">');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ boundaries.forEach(function (b, i) {
+ var c = P.of(b.el, 'bg');
+ var tc = b.textEl ? P.of(b.textEl, 'bg') : null;
+ var left = b.kind === 'lane' || b.kind === 'exception-lane' || b.kind === 'segment';
+ var style = ['rounded=' + (b.rx > 0 ? 1 : 0), b.rx > 0 ? 'absoluteArcSize=1;arcSize=' + Math.round(b.rx) : '',
+ 'fillColor=' + (c.fill || 'none'), 'strokeColor=' + (c.stroke || '#666666'), 'strokeWidth=' + b.sw,
+ c.dash ? 'dashed=1;dashPattern=' + c.dash : '',
+ 'fontColor=' + (tc && tc.fill ? tc.fill : P.textMuted), 'fontSize=9', 'fontFamily=' + FONT,
+ 'whiteSpace=wrap', 'html=1', 'verticalAlign=top', left ? 'align=left;spacingLeft=8' : ''].filter(Boolean).join(';') + ';';
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ nodes.forEach(function (n) {
+ var c = P.of(n.fillRect, 'mask');
+ var p = pm[n.id];
+ var relX = n.x - (p ? p.x : 0), relY = n.y - (p ? p.y : 0);
+ var html = n.label;
+ if (n.sublabel || n.tag) {
+ html = '' + n.label + '
'
+ + (n.sublabel ? '' + n.sublabel + '' : '')
+ + (n.sublabel && n.tag ? '
' : '')
+ + (n.tag ? '' + n.tag + '' : '');
+ }
+ var style = ['rounded=' + (n.rx > 0 ? 1 : 0), n.rx > 0 ? 'absoluteArcSize=1;arcSize=' + Math.round(n.rx) : '',
+ 'fillColor=' + (c.fill || '#d5e8d4'), 'strokeColor=' + (c.stroke || '#82b366'), 'strokeWidth=' + n.sw,
+ 'fontColor=' + P.text, 'fontSize=11', 'fontFamily=' + FONT, 'whiteSpace=wrap', 'html=1', 'verticalAlign=middle']
+ .filter(Boolean).join(';') + ';';
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ lifelines.forEach(function (ll, i) {
+ var c = P.of(ll.el, 'bg');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ edges.forEach(function (e, i) {
+ var src = nodeById[e.from], tgt = nodeById[e.to];
+ if (!src || !tgt) return;
+ var c = P.of(e.el, 'bg');
+ var lbl = edgeLabels.find(function (l) { return l.from === e.from && l.to === e.to; });
+ var lc = lbl ? P.of(lbl.el, 'bg') : null;
+ var style = ['edgeStyle=none', e.radius > 0 ? 'rounded=1;arcSize=' + e.radius * 2 : 'rounded=0',
+ 'strokeColor=' + (c.stroke || '#666666'), 'strokeWidth=' + e.sw,
+ c.dash ? 'dashed=1;dashPattern=' + c.dash : '',
+ 'endArrow=block', 'endFill=1', 'endSize=' + Math.max(4, Math.round(10 * e.sw)),
+ 'fontColor=' + (lc && lc.fill ? lc.fill : P.textMuted), 'fontSize=8', 'fontFamily=' + FONT,
+ 'labelBackgroundColor=' + P.mask, 'html=1'].filter(Boolean).join(';');
+ var exit = borderFrac(e.pts[0], src), entry = borderFrac(e.pts[e.pts.length - 1], tgt);
+ if (exit) style += ';exitX=' + exit[0] + ';exitY=' + exit[1] + ';exitDx=0;exitDy=0';
+ if (entry) style += ';entryX=' + entry[0] + ';entryY=' + entry[1] + ';entryDx=0;entryDy=0';
+ L.push(' ');
+ L.push(' ');
+ if (lbl && e.label) {
+ var t = fractionAlong(e.pts, [lbl.x, lbl.y]);
+ L.push(' ');
+ }
+ if (e.pts.length > 2) {
+ L.push(' ');
+ for (var p = 1; p < e.pts.length - 1; p++) L.push(' ');
+ L.push(' ');
+ }
+ L.push(' ');
+ L.push(' ');
+ });
+ L.push(' ');
+ L.push('' + 'mxGraphModel>');
+ return L.join('\n');
+ }
+
+ // ---- draw.io sequence export ----------------------------------------
+ // Sequence geometry is a timeline, not a node graph: participants are
+ // exported as draw.io UML lifelines spanning the full timeline so each
+ // message's authored y survives as an exit/entry fraction, activation
+ // bars become lifeline children, and segment bands keep their labels.
+ // Mirrors buildDrawioXmlSequence in renderers/shared/svg-to-drawio.mjs.
+ function drawioKindPair(kind) {
+ var style = DRAWIO_KIND_STYLE[kind] || DRAWIO_KIND_STYLE.backend;
+ var fill = style.match(/fillColor=(#[0-9a-f]{6})/i);
+ var stroke = style.match(/strokeColor=(#[0-9a-f]{6})/i);
+ return { fill: fill ? fill[1] : null, stroke: stroke ? stroke[1] : null };
+ }
+ function drawioBuildSequenceXml(strict) {
+ var svgEl = document.querySelector('.diagram-container svg');
+ if (!svgEl) throw new Error('No diagram SVG found.');
+ var P = drawioStrictPalette(svgEl);
+ var FONT = 'JetBrains Mono,ui-monospace,Menlo,monospace';
+ var vb = (svgEl.getAttribute('viewBox') || '0 0 1000 700').split(/\s+/);
+ var vw = Math.round(+vb[2]), vh = Math.round(+vb[3]);
+ // -- participants ---------------------------------------------------
+ var nodes = [], nodeById = {};
+ svgEl.querySelectorAll('g[id^="node-"]').forEach(function (g) {
+ var fillRect = null;
+ g.querySelectorAll('rect').forEach(function (r) {
+ var cls = r.getAttribute('class') || '';
+ if (!fillRect && cls.indexOf('c-') === 0 && cls !== 'c-mask') fillRect = r;
+ });
+ var geom = fillRect || g.querySelector('rect');
+ if (!geom) return;
+ var n = {
+ id: g.getAttribute('data-node-id'),
+ label: g.getAttribute('data-node-label') || '',
+ sublabel: g.getAttribute('data-node-sublabel'),
+ kind: g.getAttribute('data-node-kind') || 'backend',
+ x: +geom.x.baseVal.value, y: +geom.y.baseVal.value,
+ width: +geom.width.baseVal.value, height: +geom.height.baseVal.value,
+ rx: +(fillRect ? fillRect.getAttribute('rx') : 0) || 0,
+ sw: +(fillRect ? fillRect.getAttribute('stroke-width') : 1.5) || 1.5,
+ fillRect: fillRect || geom
+ };
+ nodes.push(n); nodeById[n.id] = n;
+ });
+ // -- timeline span: deepest dashed lifeline foot ---------------------
+ var footY = 0;
+ svgEl.querySelectorAll('path').forEach(function (p) {
+ if (p.hasAttribute('data-composition-points')) return;
+ var d = p.getAttribute('d') || '';
+ var m = d.match(/^M\s*(\d+\.?\d*)\s+(\d+\.?\d*)\s+L\s*\1\s+(\d+\.?\d*)$/);
+ if (m && getComputedStyle(p).strokeDasharray !== 'none') footY = Math.max(footY, +m[3]);
+ });
+ if (!footY) {
+ nodes.forEach(function (n) { footY = Math.max(footY, n.y + n.height + 60); });
+ if (!footY) footY = 400;
+ }
+ // -- segment bands + labels (labels dodge messages, live elsewhere) --
+ var segments = [];
+ var segLabels = {};
+ svgEl.querySelectorAll('g[data-graph-role="segment-label"]').forEach(function (g) {
+ segLabels[g.getAttribute('data-segment-id')] = (g.textContent || '').trim();
+ });
+ svgEl.querySelectorAll('rect[data-graph-role="structural-frame"]').forEach(function (r) {
+ var sid = r.getAttribute('data-composition-frame-id');
+ segments.push({
+ label: sid != null && segLabels[sid] ? segLabels[sid] : '',
+ x: +r.x.baseVal.value, y: +r.y.baseVal.value,
+ width: +r.width.baseVal.value, height: +r.height.baseVal.value,
+ rx: +r.getAttribute('rx') || 0, sw: +r.getAttribute('stroke-width') || 1,
+ el: r
+ });
+ });
+ // -- activation bars -------------------------------------------------
+ var activations = [];
+ svgEl.querySelectorAll('rect[data-graph-role="activation"]').forEach(function (r) {
+ var cls = r.getAttribute('class') || '';
+ if (!cls || cls === 'c-mask') return;
+ activations.push({
+ participant: r.getAttribute('data-activation-participant'),
+ x: +r.x.baseVal.value, y: +r.y.baseVal.value,
+ width: +r.width.baseVal.value, height: +r.height.baseVal.value,
+ rx: +r.getAttribute('rx') || 0, sw: +r.getAttribute('stroke-width') || 1,
+ el: r
+ });
+ });
+ // -- messages: label on the focus group, dash via computed style ----
+ var edges = [];
+ svgEl.querySelectorAll('path[data-composition-points]').forEach(function (p) {
+ var from = p.getAttribute('data-edge-from') || p.getAttribute('data-composition-edge-from');
+ var to = p.getAttribute('data-edge-to') || p.getAttribute('data-composition-edge-to');
+ if (!from || !to) return;
+ var pts = (p.getAttribute('data-composition-points') || '').split(';').filter(Boolean).map(function (pair) {
+ var c = pair.split(',');
+ return [+c[0], +c[1]];
+ });
+ if (pts.length < 2) return;
+ var label = p.getAttribute('data-edge-label') || p.getAttribute('data-composition-edge-label');
+ var owner = p.parentElement;
+ if (!label && owner && owner.tagName.toLowerCase() === 'g') label = owner.getAttribute('data-edge-label');
+ var dashParts = (getComputedStyle(p).strokeDasharray || '').split(/[\s,]+/).map(Number).filter(function (n) { return n > 0; });
+ edges.push({
+ from: from, to: to, id: p.getAttribute('data-edge-id') || p.getAttribute('data-composition-edge-id'),
+ label: label || '', pts: pts, el: p,
+ sw: +p.getAttribute('stroke-width') || 1.5,
+ dash: dashParts.length ? dashParts.join(' ') : ''
+ });
+ });
+ // -- emit ------------------------------------------------------------
+ var colorsOf = function (el, base) { return strict ? P.of(el, base) : null; };
+ var L = [];
+ L.push('');
+ L.push('<' + 'mxGraphModel dx="' + vw + '" dy="' + vh + '" grid="' + (strict ? 0 : 1) + '" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="' + vw + '" pageHeight="' + vh + '" math="0" shadow="0"' + (strict ? ' background="' + P.bg + '"' : '') + '>');
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ segments.forEach(function (s, i) {
+ var c = colorsOf(s.el, 'bg');
+ var style = ['rounded=' + (s.rx > 0 ? 1 : 0), s.rx > 0 ? 'absoluteArcSize=1;arcSize=' + Math.round(s.rx) : '',
+ 'fillColor=' + (c && c.fill ? c.fill : 'none'), 'strokeColor=' + (c && c.stroke ? c.stroke : '#b0b0b0'), 'strokeWidth=' + s.sw,
+ c && c.dash ? 'dashed=1;dashPattern=' + c.dash : '',
+ 'fontColor=' + (strict ? P.textMuted : '#666666'), 'fontSize=9', 'fontFamily=' + FONT,
+ 'whiteSpace=wrap', 'html=1', 'verticalAlign=top', 'align=left', 'spacingLeft=8'].filter(Boolean).join(';') + ';';
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ nodes.forEach(function (n) {
+ var c = colorsOf(n.fillRect, 'mask') || (strict ? null : drawioKindPair(n.kind));
+ var parts = [n.label, n.sublabel].filter(Boolean);
+ var html = parts.length > 1
+ ? '' + parts[0] + '
' + parts.slice(1).join(' · ') + ''
+ : n.label;
+ var style = ['shape=umlLifeline', 'perimeter=lifelinePerimeter', 'size=' + Math.round(n.height),
+ 'container=1', 'collapsible=0', 'recursiveResize=0', 'outlineConnect=0',
+ c && c.fill ? 'fillColor=' + c.fill : '',
+ c && c.stroke ? 'strokeColor=' + c.stroke : '',
+ 'strokeWidth=' + n.sw,
+ 'fontColor=' + (strict ? P.text : '#000000'), 'fontSize=11', 'fontFamily=' + FONT,
+ 'whiteSpace=wrap', 'html=1'].filter(Boolean).join(';') + ';';
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ activations.forEach(function (a, i) {
+ var participant = nodeById[a.participant];
+ if (!participant) return;
+ var c = colorsOf(a.el, 'mask') || (strict ? null : drawioKindPair(participant.kind));
+ var style = ['rounded=' + (a.rx > 0 ? 1 : 0), a.rx > 0 ? 'absoluteArcSize=1;arcSize=' + Math.round(a.rx) : '',
+ 'fillColor=' + (c && c.fill ? c.fill : '#ffffff'), 'strokeColor=' + (c && c.stroke ? c.stroke : '#999999'),
+ 'strokeWidth=' + a.sw, 'html=1'].filter(Boolean).join(';') + ';';
+ L.push(' ');
+ L.push(' ');
+ L.push(' ');
+ });
+ edges.forEach(function (e, i) {
+ var src = nodeById[e.from], tgt = nodeById[e.to];
+ if (!src || !tgt) return;
+ var c = colorsOf(e.el, 'bg');
+ var frac = function (y, cell) { return Math.max(0, Math.min(1, (y - cell.y) / (footY - cell.y))).toFixed(3); };
+ var exitY = frac(e.pts[0][1], src), entryY = frac(e.pts[e.pts.length - 1][1], tgt);
+ var style = ['edgeStyle=none', 'rounded=0',
+ 'strokeColor=' + (c && c.stroke ? c.stroke : '#666666'), 'strokeWidth=' + e.sw,
+ e.dash ? 'dashed=1;dashPattern=' + e.dash : '',
+ 'endArrow=block', 'endFill=1', 'endSize=' + Math.max(4, Math.round(10 * e.sw)),
+ 'fontColor=' + (strict ? P.textMuted : '#666666'), 'fontSize=8', 'fontFamily=' + FONT,
+ 'labelBackgroundColor=' + (strict ? P.mask : '#ffffff'), 'html=1']
+ .filter(Boolean).join(';')
+ + ';exitX=0.5;exitY=' + exitY + ';exitDx=0;exitDy=0'
+ + ';entryX=0.5;entryY=' + entryY + ';entryDx=0;entryDy=0;';
+ L.push(' ');
+ // Label geometry y=-1 lifts the label one label-height above the
+ // line, matching the Archify artifact.
+ L.push(' ');
+ if (e.pts.length > 2) {
+ L.push(' ');
+ for (var p = 1; p < e.pts.length - 1; p++) L.push(' ');
+ L.push(' ');
+ }
+ L.push(' ');
+ L.push(' ');
+ });
+ L.push(' ');
+ L.push('' + 'mxGraphModel>');
+ return L.join('\n');
+ }
+ // Public entry: always a light-themed .drawio file, independent of the
+ // viewer's current theme (and the browser preference behind it).
+ // Sequence artifacts carry timeline geometry and route through the
+ // dedicated sequence builder instead.
+ function drawioBuildStrictXml() {
+ var isSequence = document.documentElement.getAttribute('data-diagram-type') === 'sequence';
+ return withLightTheme(function () {
+ return isSequence ? drawioBuildSequenceXml(true) : buildStrictXmlLight();
+ });
+ }
+
function runExport(format) {
var base = diagramFilename();
close(true);
@@ -6724,6 +7423,30 @@ Security
recordExportReceipt('svg', blob, d.canonicalStateClean);
download(blob, base + '.svg');
})
+ : format === 'drawio'
+ ? Promise.resolve(serializeSvg(1, { autoTheme: true })).then(function (d) {
+ // Sequence artifacts need live computed styles (timeline
+ // geometry), so they read the live SVG instead of the
+ // serialized string.
+ var isSequence = document.documentElement.getAttribute('data-diagram-type') === 'sequence';
+ var xml = isSequence ? drawioBuildSequenceXml(false) : archifySvgToDrawio(d.svgString);
+ var blob = new Blob([xml], { type: 'application/xml;charset=utf-8' });
+ recordExportReceipt('drawio', blob, d.canonicalStateClean);
+ download(blob, base + '.drawio');
+ toast('Downloaded draw.io');
+ })
+ : format === 'drawio-strict'
+ ? Promise.resolve(serializeSvg(1, { autoTheme: true })).then(function (d) {
+ // Strict reads the live SVG but force-resolves the light
+ // theme, so the file never follows the reader's current
+ // theme; serializeSvg only supplies the canonical-state
+ // receipt.
+ var xml = drawioBuildStrictXml();
+ var blob = new Blob([xml], { type: 'application/xml;charset=utf-8' });
+ recordExportReceipt('drawio-strict', blob, d.canonicalStateClean);
+ download(blob, base + '-strict.drawio');
+ toast('Downloaded draw.io (strict)');
+ })
: format === 'webm'
? recordWebm().then(function (blob) {
document.documentElement.setAttribute('data-last-motion-bytes', String(blob.size));
diff --git a/examples/web-app-strict.drawio b/examples/web-app-strict.drawio
new file mode 100644
index 0000000..10b9c6a
--- /dev/null
+++ b/examples/web-app-strict.drawio
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/web-app.drawio b/examples/web-app.drawio
new file mode 100644
index 0000000..2daac24
--- /dev/null
+++ b/examples/web-app.drawio
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/web-app.html b/examples/web-app.html
index b454244..d3d6579 100644
--- a/examples/web-app.html
+++ b/examples/web-app.html
@@ -1,5 +1,5 @@
-
+
@@ -3424,6 +3424,11 @@
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 2.5l7 4v7l-7 4-7-4v-7zM3 6.5l7 4 7-4M10 10.5v7' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
}
+ .toolbar .export-menu button[data-format="drawio"]::before,
+ .toolbar .export-menu button[data-format="drawio-strict"]::before {
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Crect x='2.5' y='2.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Crect x='11.5' y='12.5' width='6' height='5' rx='1' fill='none' stroke='black' stroke-width='1.7'/%3E%3Cpath d='M8.5 5h6v7.5' fill='none' stroke='black' stroke-width='1.7'/%3E%3C/svg%3E");
+ }
.toolbar .export-menu button[data-format="share-card"],
.toolbar .export-menu button[data-action="route-share-card"],
.toolbar .export-menu button[data-action="reach-share-card"] {
@@ -4825,6 +4830,8 @@
@@ -6602,7 +6609,7 @@