Files
Community_Nursing_Atlas/???????_????_? ????? (1).html
T
2026-10-06 16:06:21 +09:00

2547 lines
108 KiB
HTML

<!doctype html>
<html lang="ko" data-visualize-standalone>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval' blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; style-src 'unsafe-inline' blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; img-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; font-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; media-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; worker-src blob:; connect-src blob: data:; frame-src 'self'; object-src 'none'; base-uri 'none'; form-action 'none'">
<title>지역사회간호학 · 개념 우주</title>
<style>:root{color-scheme:dark;background:#070c1b}html,body{margin:0}body{box-sizing:border-box;padding:16px;background:inherit}iframe{display:block;width:100%;max-width:1120px;height:calc(100vh - 2rem);margin:0 auto;border:0}</style>
</head>
<body>
<iframe id="codex-visualization" sandbox="allow-scripts" referrerpolicy="no-referrer" title="지역사회간호학 · 개념 우주" data-srcdoc="&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot; data-visualize-standalone&gt;
&lt;head&gt;
&lt;meta charset=&quot;utf-8&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
&lt;meta name=&quot;referrer&quot; content=&quot;no-referrer&quot;&gt;
&lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;default-src &#x27;none&#x27;; script-src &#x27;unsafe-inline&#x27; &#x27;unsafe-eval&#x27; &#x27;wasm-unsafe-eval&#x27; blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; style-src &#x27;unsafe-inline&#x27; blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; img-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; font-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; media-src blob: data: https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://esm.sh https://fonts.bunny.net https://fonts.googleapis.com https://fonts.gstatic.com https://unpkg.com; worker-src blob:; connect-src blob: data:; frame-src &#x27;none&#x27;; object-src &#x27;none&#x27;; base-uri &#x27;none&#x27;; form-action &#x27;none&#x27;&quot;&gt;
&lt;title&gt;지역사회간호학 · 개념 우주&lt;/title&gt;
&lt;style&gt;:root {
color-scheme: light dark;
background-color: var(
--background,
var(--color-background-primary, light-dark(rgb(255 255 255), rgb(24 24 24)))
) !important;
/* Agent-facing contract; keep in sync with SKILL.md. */
--background: var(
--color-background-primary,
light-dark(rgb(255 255 255), rgb(24 24 24))
);
--foreground: var(
--color-text-primary,
light-dark(rgb(26 28 31), rgb(255 255 255))
);
--card: color-mix(in oklab, var(--foreground) 5%, var(--background));
--card-foreground: var(
--color-text-primary,
light-dark(rgb(26 28 31), rgb(255 255 255))
);
--popover: var(
--color-background-secondary,
light-dark(rgb(255 255 255), rgb(45 45 45))
);
--popover-foreground: var(
--color-text-primary,
light-dark(rgb(26 28 31), rgb(255 255 255))
);
--primary: var(
--color-text-info,
light-dark(rgb(51 156 255), rgb(131 195 255))
);
--primary-foreground: var(
--color-text-inverse,
light-dark(rgb(255 255 255), rgb(13 13 13))
);
--secondary: var(
--color-background-secondary,
light-dark(rgb(255 255 255 / 96%), rgb(54 54 54 / 96%))
);
--secondary-foreground: var(
--color-text-primary,
light-dark(rgb(26 28 31), rgb(255 255 255))
);
--muted: color-mix(in srgb, var(--foreground) 10%, transparent);
--muted-foreground: var(
--color-text-secondary,
light-dark(rgb(26 28 31 / 49.4%), rgb(255 255 255 / 49.8%))
);
--accent: var(
--color-background-info,
light-dark(rgb(229 242 255), rgb(13 39 63))
);
--accent-foreground: var(
--color-text-info,
light-dark(rgb(51 156 255), rgb(131 195 255))
);
--destructive: var(
--color-text-warning,
light-dark(rgb(226 85 7), rgb(255 133 73))
);
--border: var(
--color-border-secondary,
light-dark(rgb(26 28 31 / 8%), rgb(255 255 255 / 8.2%))
);
--input: var(
--color-border-primary,
light-dark(
rgb(26 28 31 / 11.8%),
color-mix(in oklab, rgb(0 0 0) 10%, transparent)
)
);
--ring: var(
--color-ring-primary,
light-dark(rgb(51 156 255), rgb(131 195 255 / 76%))
);
--font-size-base: var(--font-text-md-size, 14px);
--blue: light-dark(rgb(51 156 255), rgb(51 156 255));
--orange: light-dark(rgb(226 85 7), rgb(251 106 34));
--green: light-dark(rgb(0 162 64), rgb(64 201 119));
--red: light-dark(rgb(224 46 42), rgb(255 103 100));
--purple: light-dark(rgb(146 79 247), rgb(173 123 249));
--yellow: light-dark(rgb(255 195 0), rgb(255 210 64));
--viz-series-1: var(--primary);
--viz-series-2: light-dark(rgb(243 136 59), rgb(245 154 86));
--viz-series-3: light-dark(rgb(93 201 119), rgb(116 213 139));
--viz-series-4: light-dark(rgb(235 119 177), rgb(240 143 192));
--viz-series-5: light-dark(rgb(155 121 236), rgb(170 145 239));
--viz-series-6: light-dark(rgb(58 185 177), rgb(90 203 194));
/* Internal implementation variables; not part of the agent contract. */
--font-sans: -apple-system, system-ui, &quot;Segoe UI&quot;, sans-serif;
--font-mono:
ui-monospace, SFMono-Regular, &quot;SF Mono&quot;, Menlo, Consolas, &quot;Liberation Mono&quot;,
monospace;
--font-size-normal: max(11px, var(--font-size-base));
--font-size-tooltip: calc(var(--font-size-base) - 1px);
--font-size-small: max(11px, calc(var(--font-size-base) - 2px));
--font-size-h1: calc(var(--font-size-normal) * 1.7142857143);
--font-size-h2: calc(var(--font-size-normal) * 1.4285714286);
--font-size-h3: calc(var(--font-size-normal) * 1.2857142857);
--font-weight-normal: 430;
--font-weight-medium: 500;
--icon-size: 16px;
--line-height-normal: calc(var(--font-size-normal) * 1.5);
--line-height-tooltip: calc(var(--font-size-tooltip) * 1.4285714286);
--line-height-small: calc(var(--font-size-small) + 4px);
--corner-shape: round;
--radius: var(--border-radius-lg, 10px);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-2xl: calc(var(--radius) * 1.6);
--radius-full: 9999px;
--shadow-sm: 0 1px 2px -1px rgb(0 0 0 / 8%);
--checkmark-image: url(&quot;data:image/svg+xml,%3Csvg xmlns=&#x27;http://www.w3.org/2000/svg&#x27; viewBox=&#x27;0 0 17 17&#x27;%3E%3Cpath d=&#x27;M12.8961 3.64101C13.1297 3.41418 13.4984 3.37523 13.7779 3.56581C14.0571 3.75635 14.1554 4.11331 14.0299 4.41347L13.9615 4.53847L7.71151 13.7045C7.59411 13.8767 7.4063 13.9877 7.19881 14.0072C6.99136 14.0267 6.78564 13.9533 6.63826 13.806L2.88826 10.056L2.79842 9.9457C2.6192 9.67407 2.64927 9.30496 2.88826 9.06581C3.12738 8.82669 3.49647 8.79676 3.76815 8.97597L3.8785 9.06581L7.03084 12.2182L12.8053 3.74941L12.8961 3.64101Z&#x27;/%3E%3C/svg%3E&quot;);
/* Legacy aliases; not part of the current agent contract. */
--viz-bg: transparent;
--viz-panel: var(--card);
--viz-border: var(--border);
--viz-text: var(--foreground);
--viz-muted: var(--muted-foreground);
--viz-accent: var(--primary);
--viz-accent-text: var(--primary-foreground);
--viz-accent-bg: var(--accent);
--viz-font-size: var(--font-size-base);
--viz-warning: var(--destructive);
}
:root[data-theme=&quot;light&quot;] {
color-scheme: light;
}
:root[data-theme=&quot;dark&quot;] {
color-scheme: dark;
}
* {
box-sizing: border-box;
}
html &gt; body {
/* Preserve MCP aliases for older inline renderers without creating :root cycles. */
--color-background-primary: var(
--background,
light-dark(rgb(255 255 255), rgb(24 24 24))
);
--color-text-primary: var(
--foreground,
light-dark(rgb(26 28 31), rgb(255 255 255))
);
--color-border-secondary: var(
--border,
light-dark(rgb(26 28 31 / 8%), rgb(255 255 255 / 8.2%))
);
margin: 0;
padding: var(--visualize-paint-gutter, 0px);
color: var(--foreground);
background: transparent !important;
font-family: var(--font-sans) !important;
font-size: var(--font-size-normal);
font-weight: var(--font-weight-normal);
line-height: var(--line-height-normal);
}
:root:not([data-app-block-surface], [data-visualize-standalone]) &gt; body {
padding: 12px;
}
.cursor-interaction {
cursor: var(--cursor-interaction, pointer);
}
a {
color: color-mix(in srgb, var(--viz-accent) 80%, var(--viz-text) 20%);
cursor: var(--cursor-interaction, pointer);
font-weight: var(--font-weight-medium, 500);
text-decoration: none;
text-underline-offset: 2px;
}
a:is(:hover, :focus-visible) {
text-decoration-line: underline;
text-decoration-style: dashed;
text-decoration-thickness: 0.5px;
}
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin-block: 0;
}
h1 {
font-size: var(--font-size-h1);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
h2 {
font-size: var(--font-size-h2);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
h3,
h4,
h5,
h6 {
font-size: var(--font-size-h3);
font-weight: var(--font-weight-medium);
line-height: 1.3;
}
b,
strong,
th {
font-weight: var(--font-weight-medium);
}
code:not(pre code) {
display: inline;
padding: 1px 6px;
border-radius: var(--radius-sm);
corner-shape: var(--corner-shape);
background: var(--muted);
font-family: var(--font-mono);
font-size: 0.92em;
overflow-wrap: anywhere;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
word-break: break-word;
}
.table-responsive {
width: 100%;
overflow-x: auto;
scrollbar-width: thin;
}
.table {
width: 100%;
border-collapse: collapse;
color: var(--foreground);
font: inherit;
text-align: start;
}
.table :is(th, td) {
padding-block: 10px;
padding-inline: 0 24px;
overflow-wrap: anywhere;
border-bottom: 1px solid var(--border);
text-align: start;
vertical-align: top;
}
.table-responsive &gt; .table :is(th, td) {
overflow-wrap: break-word;
}
.table :is(th, td):last-child {
padding-inline-end: 0;
}
.table :is(caption, thead th) {
font-weight: 600;
}
.table thead th {
padding-block: 8px;
border-bottom-color: color-mix(in srgb, var(--foreground) 16%, transparent);
}
.table tbody tr:last-child :is(th, td) {
border-bottom: 0;
}
.table.table-sm :is(th, td) {
padding-block: 6px;
}
.table.table-sm :is(th, td):not(:last-child) {
padding-inline-end: 16px;
}
.table :is(.text-end, [align=&quot;right&quot;]) {
text-align: end;
font-variant-numeric: tabular-nums;
}
.table :is(.text-center, [align=&quot;center&quot;]) {
text-align: center;
}
.table .text-nowrap {
white-space: nowrap;
}
#widget {
display: flex;
flex-direction: column;
gap: 12px;
width: 100%;
padding: 0;
background: transparent !important;
}
.card {
min-width: 0;
padding: 12px;
overflow: hidden;
overflow-wrap: break-word;
border-radius: var(--radius-2xl);
corner-shape: var(--corner-shape);
color: var(--card-foreground);
background: var(--card);
}
.widget {
min-width: 0;
overflow: hidden;
border: 0;
border-radius: var(--radius-lg);
corner-shape: var(--corner-shape);
box-shadow: 0 0 0 0.5px var(--border-strong, var(--border));
color: var(--foreground);
background: var(--background);
}
#widget &gt; :not(.card, .widget, .progress) {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 0 !important;
background: transparent !important;
box-shadow: none !important;
}
.tooltip {
position: fixed;
z-index: 50;
top: 0;
left: 0;
width: max-content;
max-width: min(
20rem,
var(--tooltip-available-width, calc(100vw - 10px)),
calc(100vw - 10px)
);
max-height: min(
var(--tooltip-available-height, calc(100vh - 10px)),
calc(100vh - 10px)
);
padding: 4px 8px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
corner-shape: var(--corner-shape);
color: var(--popover-foreground);
background: var(--popover);
box-shadow: none;
font-size: var(--font-size-tooltip);
line-height: var(--line-height-tooltip);
overflow-wrap: break-word;
white-space: normal;
pointer-events: none;
user-select: none;
}
/* The shared runtime owns data-tooltip; suppress AppBlock&#x27;s pseudo-tooltip. */
main [data-tooltip]::after {
content: none;
}
.viz-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max(180px, 24%), 1fr));
gap: 10px;
}
.viz-stat {
display: flex;
flex-direction: column;
gap: 2px;
}
.viz-stat-value {
font-size: var(--font-size-h2);
font-weight: var(--font-weight-medium);
line-height: 1.25;
}
.viz-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
hr {
width: 100%;
height: 1px;
margin-block: 6px;
border: 0;
background: var(--border);
}
.nav {
display: flex;
min-width: 0;
align-items: center;
gap: 4px;
}
.progress {
display: flex;
height: 8px;
margin-block: calc((var(--line-height-normal, 1.5em) - 8px) / 2);
overflow: hidden;
border: 0;
border-radius: var(--radius-full);
background: var(--muted);
}
.progress-bar:only-child {
border-radius: var(--radius-full);
}
.progress-bar {
height: 100%;
flex-shrink: 0;
border: 0;
background: var(--viz-series-1);
}
.viz-badge {
padding: 3px 8px;
border-radius: var(--radius-full);
color: var(--accent-foreground);
background: var(--accent);
font-weight: var(--font-weight-medium);
}
small,
.text-small,
.viz-badge {
font-size: var(--font-size-small);
line-height: var(--line-height-small);
}
.text-muted {
color: var(--muted-foreground);
}
.tabular-nums {
font-variant-numeric: tabular-nums;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.viz-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.viz-controls &gt; .form-label {
display: grid;
min-width: min(100%, 260px);
flex: 1 1 280px;
grid-template-columns: minmax(0, 1fr) auto;
align-items: baseline;
gap: 2px 12px;
margin-bottom: 0;
}
.viz-controls &gt; .form-label &gt; :is(.form-control, .form-range, .form-select) {
grid-column: 1 / -1;
}
.btn,
.nav-link,
.form-check-input,
.form-control,
.form-range,
.form-select {
font: inherit;
}
.btn,
.nav-link {
appearance: button;
display: inline-flex;
inline-size: fit-content;
max-inline-size: 100%;
min-height: 28px;
align-items: center;
justify-content: center;
gap: 4px;
margin: 0;
padding: 0 8px;
-webkit-app-region: no-drag;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
corner-shape: var(--corner-shape);
color: var(--secondary-foreground);
background: var(--secondary);
cursor: var(--cursor-interaction, pointer);
text-align: center;
text-decoration: none;
white-space: nowrap;
user-select: none;
-webkit-font-smoothing: antialiased;
}
.btn:is(.btn-block, .viz-tile) {
inline-size: 100%;
}
.btn.viz-tile {
min-width: 0;
overflow-wrap: anywhere;
white-space: normal;
}
a.btn {
cursor: var(--cursor-interaction, pointer);
text-decoration: none;
}
.btn:not(:disabled):hover,
.nav-link:not(:disabled):not([aria-disabled=&quot;true&quot;]):hover {
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.btn-primary {
border-color: transparent;
color: var(--primary-foreground);
background: var(--foreground);
background-clip: padding-box;
}
.btn-primary .text-muted {
color: color-mix(in srgb, var(--primary-foreground) 50%, transparent);
}
.btn-primary:not(:disabled):hover {
background: color-mix(in srgb, var(--foreground) 80%, transparent);
background-clip: padding-box;
}
.btn-ghost {
border-color: transparent;
color: var(--muted-foreground);
background: transparent;
}
.btn-ghost:not(:disabled):hover {
color: var(--foreground);
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.nav-pills .nav-link {
min-width: 0;
border-color: transparent;
color: var(--muted-foreground);
background: transparent;
}
.nav-justified .nav-link {
flex: 1 1 0;
overflow-wrap: anywhere;
white-space: normal;
}
.nav-pills .nav-link.active {
color: var(--foreground);
background: var(--muted);
}
/* Shared preview chrome; product styles stay inside each variant. */
.viz-dotted-background {
background-image: radial-gradient(
circle,
color-mix(in srgb, var(--foreground) 14%, transparent) 0.8px,
transparent 0.8px
);
background-position: center;
background-size: 16px 16px;
}
.viz-carousel {
position: relative;
min-width: 0;
}
.viz-carousel:has(&gt; .viz-carousel-controls) {
min-height: calc(60px + var(--viz-carousel-picker-height, 0px));
padding-bottom: 60px;
}
.viz-carousel &gt; [data-variant][hidden] {
display: none !important;
}
.viz-carousel &gt; [data-variant] {
isolation: isolate;
}
.viz-carousel-controls {
--viz-carousel-hover: light-dark(
color-mix(in srgb, var(--foreground) 5.4%, transparent),
color-mix(in srgb, var(--foreground) 7.8%, transparent)
);
position: absolute;
z-index: 2;
bottom: 8px;
left: 50%;
display: flex;
width: max-content;
/* Leave room for the host&#x27;s design-controls launcher at the trailing edge. */
max-width: calc(100% - 96px);
align-items: center;
gap: 0;
padding: 0;
border: 1px solid var(--border);
border-radius: 16px;
color: var(--foreground);
background: var(--background);
font-family: var(--font-sans);
/* Match the settings dropdown chrome independently of the content font size. */
font-size: 13px;
font-weight: var(--font-weight-normal);
line-height: 18px;
transform: translateX(-50%);
user-select: none;
}
/* Keep authored mockup button styles out of the shared carousel chrome. */
.viz-carousel-controls button,
.viz-carousel-picker &gt; summary {
display: flex !important;
height: auto !important;
min-height: 30px;
align-items: center !important;
justify-content: center !important;
gap: 4px;
padding: 0 6px !important;
-webkit-app-region: no-drag;
border: 0 !important;
border-radius: 14px !important;
color: inherit !important;
background: transparent !important;
font: inherit !important;
-webkit-tap-highlight-color: transparent;
}
.viz-carousel-controls &gt; button,
.viz-carousel-picker &gt; summary {
position: relative;
isolation: isolate;
}
.viz-carousel-controls &gt; button {
--icon-size: 16px;
width: 30px !important;
padding: 0 !important;
}
.viz-carousel-picker &gt; summary {
padding-inline: 12px !important;
}
.viz-carousel-controls .viz-carousel-caret {
width: 14px;
height: 14px;
flex: none;
margin: 0;
color: inherit;
transform: rotate(180deg);
}
.viz-carousel-controls &gt; button &gt; svg,
.viz-carousel-controls .viz-carousel-caret,
.viz-carousel-count {
opacity: 0.5;
}
.viz-carousel-controls &gt; button::before,
.viz-carousel-picker &gt; summary::before {
position: absolute;
z-index: -1;
inset: 3px;
border-radius: 12px;
content: &quot;&quot;;
pointer-events: none;
}
.viz-carousel-controls &gt; button:hover::before,
.viz-carousel-picker &gt; summary:hover::before {
background: var(--viz-carousel-hover);
}
.viz-carousel-controls &gt; button,
.viz-carousel-controls [data-lucide],
.viz-carousel-controls .lucide {
flex-shrink: 0;
}
.viz-carousel-picker {
min-width: 0;
}
.viz-carousel-picker &gt; summary {
min-width: 0;
list-style: none;
}
.viz-carousel-picker &gt; summary::-webkit-details-marker {
display: none;
}
.viz-carousel-count {
flex-shrink: 0;
white-space: nowrap;
}
.viz-carousel-name {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.viz-carousel-options {
position: absolute;
right: 0;
bottom: calc(100% + 4px);
left: 0;
display: flex;
max-height: 240px;
flex-direction: column;
gap: 2px;
overflow: auto;
padding: 4px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--background);
box-shadow: 0 4px 12px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 25%));
}
.viz-carousel-options &gt; button {
border-radius: max(0px, calc(var(--radius-lg) - 5px)) !important;
width: 100% !important;
min-height: 28px;
justify-content: flex-start !important;
padding: 4px 8px !important;
font-size: var(--font-size-small) !important;
text-align: start;
white-space: normal;
overflow-wrap: anywhere;
}
.viz-carousel-options &gt; [aria-current=&quot;true&quot;],
.viz-carousel-options &gt; button:hover {
background: var(--viz-carousel-hover) !important;
}
.btn:disabled,
.nav-link:disabled,
.nav-link[aria-disabled=&quot;true&quot;] {
cursor: not-allowed;
opacity: 0.4;
}
[data-lucide] {
stroke-width: 1.6;
}
.form-label {
display: block;
margin-bottom: 6px;
color: var(--foreground);
}
.form-control {
display: block;
width: 100%;
min-height: 28px;
padding: 0 8px;
outline: none;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
corner-shape: var(--corner-shape);
color: var(--foreground);
background: var(--secondary);
}
.form-control::placeholder {
color: var(--muted-foreground);
}
.form-control[type=&quot;file&quot;] {
padding: 0;
overflow: hidden;
cursor: var(--cursor-interaction, pointer);
}
.form-control[type=&quot;file&quot;]::file-selector-button {
min-height: 26px;
margin-right: 8px;
padding: 0 8px;
border: 0;
border-right: 1px solid var(--input);
color: var(--secondary-foreground);
background: var(--secondary);
cursor: inherit;
font: inherit;
}
.form-control[type=&quot;file&quot;]:not(:disabled):hover::file-selector-button {
background: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.form-control-color[type=&quot;color&quot;] {
width: 40px;
height: 28px;
padding: 3px;
cursor: var(--cursor-interaction, pointer);
}
.form-control-color[type=&quot;color&quot;]::-webkit-color-swatch-wrapper {
padding: 0;
}
.form-control-color[type=&quot;color&quot;]::-webkit-color-swatch {
border: 0;
border-radius: calc(var(--radius-lg) - 4px);
corner-shape: var(--corner-shape);
}
textarea.form-control {
height: auto;
min-height: 72px;
padding: 8px 10px;
resize: vertical;
}
.form-control:focus-visible {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-control:disabled,
.form-select:disabled {
cursor: not-allowed;
opacity: 0.4;
}
.form-select {
appearance: none;
display: block;
width: 100%;
min-height: 28px;
margin: 0;
padding: 0 32px 0 8px;
outline: none;
border: 1px solid var(--input);
border-radius: var(--radius-lg);
corner-shape: var(--corner-shape);
color: var(--foreground);
background-color: var(--secondary);
background-image:
linear-gradient(45deg, transparent 50%, var(--muted-foreground) 50%),
linear-gradient(135deg, var(--muted-foreground) 50%, transparent 50%);
background-position:
calc(100% - 14px) 50%,
calc(100% - 10px) 50%;
background-repeat: no-repeat;
background-size: 4px 4px;
cursor: var(--cursor-interaction, default);
}
.form-select:not(:disabled):hover {
background-color: color-mix(in srgb, var(--foreground) 6%, var(--secondary));
}
.form-select:focus-visible {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-check {
display: flex;
min-height: 20px;
align-items: center;
gap: 6px;
}
.form-check-input {
appearance: none;
width: 14px;
height: 14px;
flex: 0 0 auto;
margin: 0;
border: 1px solid var(--input);
color: var(--primary-foreground);
background-color: transparent;
cursor: var(--cursor-interaction, default);
transition:
background-color 150ms,
border-color 150ms,
box-shadow 150ms;
}
.form-check:not(.form-switch) .form-check-input[type=&quot;checkbox&quot;] {
border-color: var(--input);
border-radius: var(--radius-sm);
corner-shape: var(--corner-shape);
background-color: var(--secondary);
box-shadow: var(--shadow-sm);
}
.form-check:not(.form-switch)
.form-check-input:not(:disabled):not(:checked):hover {
background-color: var(--card);
}
.form-check:not(.form-switch) .form-check-input[type=&quot;checkbox&quot;]:checked {
border-color: var(--primary);
background-color: var(--primary);
}
.form-check:not(.form-switch)
.form-check-input[type=&quot;checkbox&quot;]:checked::before {
display: block;
width: 100%;
height: 100%;
background: var(--primary-foreground);
content: &quot;&quot;;
mask: var(--checkmark-image) center / 12px 12px no-repeat;
}
.form-check-input[type=&quot;radio&quot;] {
width: 14px;
height: 14px;
border-radius: var(--radius-full);
}
.form-check-input[type=&quot;radio&quot;]:checked {
border: 2px solid var(--primary);
background:
radial-gradient(circle, var(--primary-foreground) 0 2.5px, transparent 3px),
var(--primary);
}
.form-check:not(.form-switch) .form-check-input:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
.form-check:not(.form-switch) .form-check-input:disabled {
cursor: not-allowed;
pointer-events: none;
}
.form-check:not(.form-switch) .form-check-input:disabled + .form-check-label {
cursor: not-allowed;
}
.form-switch .form-check-input:disabled,
.form-switch .form-check-input:disabled + .form-check-label {
cursor: not-allowed;
opacity: 0.6;
}
.form-check-label {
color: var(--foreground);
cursor: var(--cursor-interaction, default);
}
.form-switch .form-check-input {
position: relative;
width: 32px;
height: 20px;
border: 0;
border-radius: var(--radius-full);
background: var(--muted);
box-shadow: none;
transition: background-color 200ms cubic-bezier(0, 0, 0.2, 1);
}
.form-switch .form-check-input::before {
position: absolute;
top: 50%;
left: 0;
width: 16px;
height: 16px;
box-sizing: border-box;
border: 1px solid light-dark(var(--primary-foreground), var(--foreground));
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
box-shadow: var(--shadow-sm);
content: &quot;&quot;;
transform: translate(2px, -50%);
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
}
.form-switch .form-check-input:checked {
background: var(--primary);
}
.form-switch .form-check-input:checked::before {
transform: translate(14px, -50%);
}
.form-switch .form-check-input:focus-visible {
box-shadow: 0 0 0 2px var(--ring);
}
.form-range {
appearance: none;
display: block;
width: 100%;
height: 28px;
flex: 1;
margin: 0;
padding: 0;
outline: none;
border: 0;
accent-color: var(--primary);
background: linear-gradient(
color-mix(in srgb, var(--foreground) 7%, transparent),
color-mix(in srgb, var(--foreground) 7%, transparent)
)
center / 100% 2px no-repeat;
}
.form-range::-webkit-slider-runnable-track {
height: 28px;
background: transparent;
}
.form-range::-webkit-slider-thumb {
appearance: none;
width: 20px;
height: 20px;
margin-top: 4px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
}
.form-range:focus-visible::-webkit-slider-thumb {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-range::-moz-range-track {
height: 28px;
background: transparent;
}
.form-range::-moz-range-thumb {
width: 20px;
height: 20px;
border: 1px solid var(--border);
border-radius: var(--radius-full);
background: light-dark(var(--primary-foreground), var(--foreground));
}
.form-range:focus-visible::-moz-range-thumb {
border-color: var(--ring);
box-shadow: inset 0 0 0 1px var(--ring);
}
.form-range:disabled {
cursor: not-allowed;
opacity: 0.4;
}
.btn:not(.btn-primary, .viz-tile):is(
[aria-pressed=&quot;true&quot;],
[aria-selected=&quot;true&quot;],
.is-selected
) {
border-color: var(--primary);
color: var(--primary-foreground);
background: var(--primary);
}
.btn.viz-tile:is([aria-pressed=&quot;true&quot;], [aria-selected=&quot;true&quot;], .is-selected) {
border-color: var(--primary);
box-shadow: inset 0 0 0 1px var(--primary);
}
.btn:focus-visible,
.nav-link:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}
.viz-carousel-controls :is(button, summary):focus-visible {
outline: 2px solid var(--ring) !important;
outline-offset: 2px !important;
}
@media (pointer: coarse) {
.viz-carousel:has(&gt; .viz-carousel-controls) {
min-height: calc(76px + var(--viz-carousel-picker-height, 0px));
padding-bottom: 76px;
}
.btn,
.nav-link,
.viz-carousel-controls button,
.viz-carousel-picker &gt; summary,
.form-control,
.form-select {
min-width: 44px;
min-height: 44px;
}
/* Keep small-screen WebKit from zooming when a text field receives focus. */
.form-control,
.form-select {
font-size: max(16px, var(--font-size-normal));
}
.form-control[type=&quot;file&quot;]::file-selector-button {
min-width: 44px;
min-height: 44px;
}
.form-control-color[type=&quot;color&quot;] {
width: 44px;
height: 44px;
}
.form-control:is(
[type=&quot;date&quot;],
[type=&quot;datetime-local&quot;],
[type=&quot;month&quot;],
[type=&quot;time&quot;],
[type=&quot;week&quot;]
) {
min-width: 0;
max-width: 100%;
padding-block: 8px;
}
.form-check {
min-height: 44px;
padding-inline: 15px;
}
.form-check-input {
position: relative;
}
.form-check-input::after {
position: absolute;
top: 50%;
left: 50%;
width: 44px;
height: 44px;
content: &quot;&quot;;
transform: translate(-50%, -50%);
}
.form-check-label {
display: inline-flex;
min-width: 44px;
min-height: 44px;
align-items: center;
}
.form-range {
height: 44px;
touch-action: pan-y;
}
.form-range::-webkit-slider-runnable-track {
height: 44px;
}
.form-range::-moz-range-track {
height: 44px;
}
.form-range::-webkit-slider-thumb {
margin-top: 12px;
}
}
svg {
display: block;
max-width: 100%;
height: auto;
}
#widget &gt; svg {
width: 100%;
}
/* Model-authored chart rules can otherwise stretch icons after these styles load. */
svg.lucide {
display: block;
width: var(--icon-size) !important;
height: var(--icon-size) !important;
flex: none;
margin: 0 !important;
stroke-width: 1.6;
}
.text-warning,
.text-destructive {
color: var(--destructive);
}
html&gt;body{padding:0}&lt;/style&gt;
&lt;style&gt;
#nursing-cosmos .nc-search-form&gt;i[data-lucide=&quot;search&quot;]{display:block;width:14px;height:14px;flex:none;border:1.7px solid #b6accd;border-radius:50%;position:relative;margin-right:5px;margin-bottom:3px}
#nursing-cosmos .nc-search-form&gt;i[data-lucide=&quot;search&quot;]:after{content:&quot;&quot;;position:absolute;width:7px;height:1.7px;background:#b6accd;right:-5px;bottom:-3px;transform:rotate(45deg);border-radius:1px}
&lt;/style&gt;&lt;/head&gt;
&lt;body&gt;
&lt;script type=&quot;application/json&quot; id=&quot;codex-visualization-widget-state&quot;&gt;__CODEX_VISUALIZATION_WIDGET_STATE__&lt;/script&gt;
&lt;script&gt;(() =&gt; {
const root = document.documentElement;
const mediaQuery = globalThis.matchMedia(&quot;(prefers-color-scheme: dark)&quot;);
const apply = Reflect.apply;
const isArray = Array.isArray;
const hasOwn = Object.hasOwn;
const parentWindow = globalThis.parent;
const stringify = JSON.stringify;
const parse = JSON.parse;
const encode = TextEncoder.prototype.encode.bind(new TextEncoder());
const initialStateElement = document.getElementById(
&quot;codex-visualization-widget-state&quot;,
);
const initialGlobals = parse(initialStateElement?.textContent ?? &quot;{}&quot;);
initialStateElement?.remove();
/** @type {Record&lt;string, unknown&gt; | null} */
let state = initialGlobals.widgetState ?? null;
const statePersistence = initialGlobals.statePersistence ?? &quot;none&quot;;
let nextStateRequestId = 0;
/** @type {Map&lt;number, { resolve: () =&gt; void; reject: (error: Error) =&gt; void; timeout: ReturnType&lt;typeof globalThis.setTimeout&gt;; serializedState: string }&gt;} */
const pendingStateWrites = new Map();
const persistenceError = () =&gt;
Object.assign(
new Error(&quot;The host could not persist the widget state update&quot;),
{
code: -32000,
data: { code: &quot;widget_state_persistence_failed&quot;, retryable: false },
},
);
/**
* @param {object} prototype
* @param {string} property
*/
const getPrototypeGetter = (prototype, property) =&gt; {
// oxlint-disable-next-line typescript/unbound-method -- invoked through captured Reflect.apply
const getter = Object.getOwnPropertyDescriptor(prototype, property)?.get;
if (getter == null) {
throw new Error(`Missing ${property} getter`);
}
return getter;
};
const getMessageData = getPrototypeGetter(MessageEvent.prototype, &quot;data&quot;);
const getMessagePorts = getPrototypeGetter(MessageEvent.prototype, &quot;ports&quot;);
const getMessageSource = getPrototypeGetter(MessageEvent.prototype, &quot;source&quot;);
// oxlint-disable-next-line typescript/unbound-method -- captured before the untrusted fragment runs
const addHostMessageListener = MessagePort.prototype.addEventListener;
// oxlint-disable-next-line typescript/unbound-method -- captured before the untrusted fragment runs
const postHostMessage = MessagePort.prototype.postMessage;
// oxlint-disable-next-line typescript/unbound-method -- captured before the untrusted fragment runs
const startHostPort = MessagePort.prototype.start;
// oxlint-disable-next-line typescript/unbound-method -- captured before the untrusted fragment runs
const stopMessagePropagation = Event.prototype.stopImmediatePropagation;
/** @type {MessagePort | null} */
let hostPort = null;
const postToHost = (type, payload) =&gt; {
if (hostPort != null) {
apply(postHostMessage, hostPort, [{ type, ...payload }]);
}
};
const openExternal = ({ href }) =&gt; {
if (globalThis.navigator.userActivation?.isActive === true) {
postToHost(&quot;open-external&quot;, { href });
}
};
const sendFollowUpMessage = ({ context, prompt, title }) =&gt; {
if (globalThis.navigator.userActivation?.isActive === true) {
postToHost(&quot;follow-up&quot;, { context, prompt, title });
}
return Promise.resolve();
};
/**
* @param {Record&lt;string, unknown&gt; | ((previous: Record&lt;string, unknown&gt; | null) =&gt; Record&lt;string, unknown&gt;)} next
* @returns {Promise&lt;void&gt;}
*/
const setWidgetState = async (next) =&gt; {
const nextState = typeof next === &quot;function&quot; ? next(state) : next;
if (
nextState == null ||
typeof nextState !== &quot;object&quot; ||
isArray(nextState)
) {
throw new TypeError(&quot;Widget state must be a JSON object&quot;);
}
const inputState = stringify(
nextState,
(_key, /** @type {unknown} */ value) =&gt; {
if (
typeof value === &quot;undefined&quot; ||
typeof value === &quot;function&quot; ||
typeof value === &quot;symbol&quot; ||
(typeof value === &quot;number&quot; &amp;&amp; !Number.isFinite(value))
) {
throw new TypeError(&quot;Widget state must be a JSON object&quot;);
}
return value;
},
);
const parsedState = parse(inputState);
if (
parsedState == null ||
typeof parsedState !== &quot;object&quot; ||
isArray(parsedState)
) {
throw new TypeError(&quot;Widget state must be a JSON object&quot;);
}
const storedState = {
modelContent: null,
privateContent: null,
...parsedState,
};
const serializedState = stringify(storedState);
if (encode(serializedState).length &gt; 16 * 1024) {
throw new TypeError(
&quot;Widget state must be a JSON object of at most 16 KiB&quot;,
);
}
if (
hasOwn(storedState, &quot;imageIds&quot;) &amp;&amp;
(!isArray(storedState.imageIds) ||
storedState.imageIds.some(
(/** @type {unknown} */ id) =&gt; typeof id !== &quot;string&quot;,
))
) {
throw new TypeError(&quot;Widget state imageIds must be an array of strings&quot;);
}
if (storedState.imageIds?.length &gt; 0) {
throw Object.assign(
new TypeError(&quot;Visualization state does not support image attachments&quot;),
{ code: -32602, data: { code: &quot;images_unsupported&quot; } },
);
}
state = storedState;
globalThis.openai.widgetState = state;
globalThis.dispatchEvent(
new CustomEvent(&quot;openai:set_globals&quot;, {
detail: { globals: { widgetState: state } },
}),
);
if (statePersistence === &quot;none&quot;) {
throw persistenceError();
}
const id = ++nextStateRequestId;
return new Promise((resolve, reject) =&gt; {
const timeout = globalThis.setTimeout(() =&gt; {
pendingStateWrites.delete(id);
reject(persistenceError());
}, 30_000);
pendingStateWrites.set(id, { resolve, reject, timeout, serializedState });
postToHost(&quot;widget-state-write&quot;, { id, state: serializedState });
});
};
const syncTheme = () =&gt; {
const theme = mediaQuery.matches ? &quot;dark&quot; : &quot;light&quot;;
root.dataset.theme = theme;
globalThis.openai = {
...globalThis.openai,
openExternal,
sendFollowUpMessage,
setWidgetState,
theme,
visualizationStyleVariables: {},
visualizationTheme: theme,
widgetState: state,
statePersistence,
stateModelContext: &quot;none&quot;,
};
globalThis.dispatchEvent(
new CustomEvent(&quot;openai:set_globals&quot;, {
detail: { globals: globalThis.openai },
}),
);
};
const sendHeight = () =&gt; {
postToHost(&quot;height&quot;, {
height: Math.ceil(
Math.max(
document.body.scrollHeight,
document.body.getBoundingClientRect().height,
),
),
});
};
globalThis.addEventListener(
&quot;message&quot;,
(event) =&gt; {
const data = apply(getMessageData, event, []);
const ports = apply(getMessagePorts, event, []);
const source = apply(getMessageSource, event, []);
if (
hostPort != null ||
source !== parentWindow ||
typeof data !== &quot;object&quot; ||
data?.type !== &quot;codex-visualization-initialize&quot; ||
!isArray(ports) ||
ports.length !== 1
) {
return;
}
apply(stopMessagePropagation, event, []);
const nextHostPort = ports[0];
try {
apply(addHostMessageListener, nextHostPort, [
&quot;message&quot;,
(event) =&gt; {
const data = apply(getMessageData, event, []);
if (typeof data === &quot;object&quot; &amp;&amp; data?.type === &quot;measure&quot;) {
sendHeight();
} else if (
typeof data === &quot;object&quot; &amp;&amp;
data?.type === &quot;widget-state-result&quot;
) {
/** @type {unknown} */
const id = data.id;
if (typeof id !== &quot;number&quot;) {
return;
}
const pending = pendingStateWrites.get(id);
if (pending != null) {
pendingStateWrites.delete(id);
globalThis.clearTimeout(pending.timeout);
if (data.ok === true) {
pending.resolve();
} else {
pending.reject(persistenceError());
}
}
}
},
]);
apply(startHostPort, nextHostPort, []);
} catch {
return;
}
hostPort = nextHostPort;
for (const [id, { serializedState }] of pendingStateWrites) {
postToHost(&quot;widget-state-write&quot;, { id, state: serializedState });
}
sendHeight();
},
{ capture: true },
);
document.currentScript?.remove();
mediaQuery.addEventListener(&quot;change&quot;, syncTheme);
globalThis.addEventListener(&quot;click&quot;, (event) =&gt; {
if (event.defaultPrevented) {
return;
}
let element = null;
if (event.target instanceof globalThis.Element) {
element = event.target;
} else if (event.target instanceof globalThis.Node) {
element = event.target.parentElement;
}
const link = element?.closest(&quot;a[href]&quot;);
const href = link?.getAttribute(&quot;href&quot;);
if (href == null) {
return;
}
if (href.startsWith(&quot;#&quot;)) {
let id;
try {
id = decodeURIComponent(href.slice(1));
} catch {
return;
}
const target = id.length === 0 ? root : document.getElementById(id);
if (
target == null ||
globalThis.navigator.userActivation?.isActive !== true
) {
return;
}
event.preventDefault();
postToHost(&quot;scroll-to&quot;, {
top: target.getBoundingClientRect().top + globalThis.scrollY,
});
return;
}
event.preventDefault();
openExternal({ href });
});
new ResizeObserver(sendHeight).observe(document.body);
syncTheme();
})();
&lt;/script&gt;
&lt;div id=&quot;nursing-cosmos&quot;&gt;
&lt;style&gt;
#nursing-cosmos{--nc-bg:#070c1b;--nc-panel:#10172a;--nc-border:#27314c;--nc-text:#edf1ff;--nc-muted:#a4b0cf;--nc-accent:#c2b4ff;color-scheme:dark;background:var(--nc-bg);color:var(--nc-text);font-family:Arial,&quot;Noto Sans KR&quot;,sans-serif;font-size:14px;line-height:1.6;border-radius:18px;overflow:hidden;isolation:isolate}
#nursing-cosmos *{box-sizing:border-box}
#nursing-cosmos button,#nursing-cosmos input{font:inherit}
#nursing-cosmos button{color:inherit}
#nursing-cosmos button:focus-visible,#nursing-cosmos input:focus-visible{outline:2px solid #d3c7ff;outline-offset:4px}
#nursing-cosmos .nc-top{padding:22px 26px 18px;display:flex;align-items:center;justify-content:space-between;gap:14px;border-bottom:1px solid #1c2540}
#nursing-cosmos .nc-brand{display:flex;align-items:center;gap:13px}
#nursing-cosmos .nc-orbit-logo{width:34px;height:34px;border:1px solid #726892;border-radius:50%;position:relative;transform:rotate(-28deg)}
#nursing-cosmos .nc-orbit-logo:before{content:&quot;&quot;;position:absolute;width:45px;height:14px;left:-7px;top:9px;border:1px solid #bca5ff;border-radius:50%}
#nursing-cosmos .nc-orbit-logo:after{content:&quot;&quot;;position:absolute;width:7px;height:7px;border-radius:50%;background:#e6ddff;right:0;top:0;box-shadow:0 0 14px #b393ff}
#nursing-cosmos .nc-brand-title{font-size:21px;font-weight:500;letter-spacing:-.7px;line-height:1.35}
#nursing-cosmos .nc-brand-sub{font-size:11px;letter-spacing:2.5px;color:var(--nc-muted);margin-top:3px}
#nursing-cosmos .nc-top-meta{display:flex;align-items:center;gap:12px;font-size:12px;color:var(--nc-muted)}
#nursing-cosmos .nc-prototype{border:1px solid #38415d;border-radius:20px;padding:3px 10px;color:#c8d0e7;white-space:nowrap}
#nursing-cosmos .nc-search-zone{padding:18px 26px 13px;display:flex;gap:18px;align-items:center}
#nursing-cosmos .nc-search-area{position:relative;flex:1;min-width:0;z-index:12}
#nursing-cosmos .nc-search-form{display:flex;align-items:center;gap:10px;border:1px solid #465077;border-radius:12px;background:#141b31;padding:10px 12px 10px 16px}
#nursing-cosmos .nc-search-form&gt;svg{width:19px;height:19px;flex:none;color:#b6accd}
#nursing-cosmos .nc-search-form input{background:transparent;border:0;outline-offset:2px;color:var(--nc-text);min-width:0;width:100%;padding:2px 0;font-size:14px}
#nursing-cosmos input::placeholder{color:#9da8c1;opacity:1}
#nursing-cosmos .nc-search-submit{background:#d2c7fa;border:0;border-radius:7px;padding:5px 14px;color:#1a1530;white-space:nowrap;font-weight:500}
#nursing-cosmos .nc-search-note{font-size:12px;color:#9da9c7;max-width:205px}
#nursing-cosmos .nc-results{position:absolute;left:0;right:0;top:calc(100% + 7px);border:1px solid #4b5577;background:#151d32;border-radius:12px;padding:8px;box-shadow:0 16px 50px #0008}
#nursing-cosmos .nc-result-head{display:flex;justify-content:space-between;align-items:center;padding:5px 8px 8px;color:#b7bfd8;font-size:12px}
#nursing-cosmos .nc-close-search{border:0;background:transparent;padding:3px 6px;color:#c7cce0}
#nursing-cosmos .nc-result{width:100%;text-align:left;display:flex;justify-content:space-between;gap:12px;padding:10px;border:0;border-radius:8px;background:transparent}
#nursing-cosmos .nc-result:hover{background:#28324e}
#nursing-cosmos .nc-result strong{font-weight:500}
#nursing-cosmos .nc-result small{display:block;color:#acb6d0;margin-top:2px;font-size:12px}
#nursing-cosmos .nc-result .nc-result-arrow{color:var(--nc-accent);flex:none}
#nursing-cosmos .nc-empty{margin:0;padding:10px;font-size:13px;color:#c5cce1}
#nursing-cosmos .nc-main{display:grid;grid-template-columns:minmax(0,1fr) 266px;min-width:0}
#nursing-cosmos .nc-map-wrap{min-width:0;position:relative}
#nursing-cosmos .nc-map-bar{position:absolute;left:24px;right:20px;top:12px;z-index:5;display:flex;align-items:center;justify-content:space-between;gap:10px}
#nursing-cosmos .nc-breadcrumb{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:12px;color:#c2cae0}
#nursing-cosmos .nc-home{padding:5px 9px;border:1px solid #36405d;background:#11192e;border-radius:7px;white-space:nowrap}
#nursing-cosmos .nc-crumb-current{color:#bdaeff;font-size:12px}
#nursing-cosmos .nc-map-label{color:#95a2bf;font-size:11px;letter-spacing:1px;white-space:nowrap}
#nursing-cosmos .nc-map{position:relative;height:620px;overflow:hidden;background:radial-gradient(ellipse at 48% 48%,#29204444,transparent 50%),radial-gradient(ellipse at 78% 15%,#143a482a,transparent 35%),#080d1c}
#nursing-cosmos .nc-starfield,#nursing-cosmos .nc-svg,#nursing-cosmos .nc-objects{position:absolute;inset:0;width:100%;height:100%}
#nursing-cosmos .nc-starfield{pointer-events:none;overflow:hidden}
#nursing-cosmos .nc-spark{position:absolute;width:2px;height:2px;background:#d3def6;border-radius:50%;opacity:.2}
#nursing-cosmos .nc-spark.big{width:3px;height:3px;opacity:.6;box-shadow:0 0 8px #d9e3ff66}
#nursing-cosmos .nc-svg{pointer-events:none;overflow:visible}
#nursing-cosmos .nc-objects{pointer-events:none}
#nursing-cosmos .nc-galaxy{position:absolute;transform:translate(-50%,-50%);width:174px;min-height:92px;border:0;background:transparent;padding:34px 5px 5px;text-align:center;pointer-events:auto;z-index:2}
#nursing-cosmos .nc-galaxy:before{content:&quot;&quot;;position:absolute;left:50%;top:14px;transform:translateX(-50%);width:18px;height:18px;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fff,var(--nc-glow) 33%,#222741 90%);box-shadow:0 0 20px 5px color-mix(in srgb,var(--nc-glow) 55%,transparent),0 0 65px 25px color-mix(in srgb,var(--nc-glow) 14%,transparent)}
#nursing-cosmos .nc-galaxy:after{content:&quot;&quot;;position:absolute;top:6px;left:50%;width:76px;height:31px;border:1px solid color-mix(in srgb,var(--nc-glow) 40%,transparent);border-radius:50%;transform:translateX(-50%) rotate(-24deg);pointer-events:none}
#nursing-cosmos .nc-galaxy:hover:before{box-shadow:0 0 25px 8px color-mix(in srgb,var(--nc-glow) 70%,transparent),0 0 70px 26px color-mix(in srgb,var(--nc-glow) 20%,transparent)}
#nursing-cosmos .nc-galaxy-name{display:block;font-size:15px;font-weight:500;letter-spacing:-.4px;text-shadow:0 2px 6px #070c1b}
#nursing-cosmos .nc-galaxy-sub{display:block;font-size:11px;color:#bbc4dc;margin-top:2px;white-space:normal}
#nursing-cosmos .nc-star-node{position:absolute;transform:translate(-50%,-50%);display:flex;flex-direction:column;align-items:center;gap:7px;padding:8px 9px;border:0;background:transparent;white-space:nowrap;pointer-events:auto;z-index:3;min-height:44px;min-width:44px}
#nursing-cosmos .nc-star-node .nc-dot{display:block;width:7px;height:7px;background:var(--nc-glow);border-radius:50%;box-shadow:0 0 11px color-mix(in srgb,var(--nc-glow) 65%,transparent);position:relative;flex:none}
#nursing-cosmos .nc-star-node .nc-node-name{font-size:12px;color:#c8d0e4;background:#090f21c9;border-radius:5px;padding:0 5px;text-shadow:0 1px 4px #000}
#nursing-cosmos .nc-star-node:hover .nc-node-name{color:#fff}
#nursing-cosmos .nc-star-node.is-selected .nc-dot{width:11px;height:11px;background:#fff;box-shadow:0 0 15px 5px var(--nc-glow)}
#nursing-cosmos .nc-star-node.is-selected .nc-dot:after{content:&quot;&quot;;position:absolute;inset:-7px;border:1px solid var(--nc-glow);border-radius:50%;opacity:.8}
#nursing-cosmos .nc-star-node.is-selected .nc-node-name{color:#f7f3ff;background:#332c4d;padding:3px 9px}
#nursing-cosmos .nc-focus-mode .nc-star-node .nc-node-name{font-size:14px;padding:3px 8px}
#nursing-cosmos .nc-focus-mode .nc-dot{width:9px;height:9px}
#nursing-cosmos .nc-focus-mode .nc-star-node.is-selected .nc-dot{width:14px;height:14px}
#nursing-cosmos .nc-focus-title{position:absolute;text-align:center;transform:translate(-50%,-50%);width:180px;pointer-events:none;z-index:1}
#nursing-cosmos .nc-focus-title .nc-planet{display:block;width:45px;height:45px;margin:0 auto 13px;border-radius:50%;background:radial-gradient(circle at 28% 23%,#e3d9ff,#9580d0 30%,#28274b 70%,#11162b);box-shadow:0 0 55px 16px #b19df327}
#nursing-cosmos .nc-focus-title strong{font-weight:500;font-size:17px;display:block}
#nursing-cosmos .nc-focus-title small{display:block;color:#9faac8;font-size:12px;margin-top:3px}
#nursing-cosmos .nc-map-bottom{padding:12px 24px 15px;display:flex;justify-content:space-between;align-items:center;gap:10px;background:#0c1223;border-top:1px solid #202940;font-size:11px;color:#a7b2ce;flex-wrap:wrap}
#nursing-cosmos .nc-legend{display:flex;gap:13px;align-items:center;flex-wrap:wrap}
#nursing-cosmos .nc-legend span{display:flex;gap:6px;align-items:center}
#nursing-cosmos .nc-legend-dot{width:5px;height:5px;display:inline-block;border-radius:50%;background:#d7c5ff;box-shadow:0 0 5px #ae9de877}
#nursing-cosmos .nc-legend-planet{width:10px;height:10px;display:inline-block;border:1px solid #c6b4f2;border-radius:50%;background:#736499}
#nursing-cosmos .nc-legend-line{display:inline-block;width:17px;height:1px;background:#b7a0f2}
#nursing-cosmos .nc-detail{background:var(--nc-panel);border-left:1px solid var(--nc-border);padding:24px 21px 18px;min-width:0}
#nursing-cosmos .nc-detail-kicker{font-size:11px;color:#c4b3ee;letter-spacing:1.2px;margin-bottom:12px;display:flex;align-items:center;gap:8px}
#nursing-cosmos .nc-detail-dot{width:6px;height:6px;border-radius:50%;background:#c3aafa;box-shadow:0 0 8px #bca3fa}
#nursing-cosmos .nc-detail-category{color:#acb7d2;font-size:12px;margin-bottom:4px}
#nursing-cosmos .nc-detail h2{font-size:23px;line-height:1.4;letter-spacing:-.8px;font-weight:500;color:#f2efff;margin:0 0 6px}
#nursing-cosmos .nc-alias{font-size:11px;color:#98a7c6;min-height:18px}
#nursing-cosmos .nc-detail-section{margin-top:24px}
#nursing-cosmos .nc-detail h3{font-size:11px;font-weight:500;letter-spacing:1px;color:#b9a5ed;margin:0 0 8px}
#nursing-cosmos .nc-detail p{font-size:13px;line-height:1.8;color:#d9dfef;margin:0;word-break:keep-all;overflow-wrap:anywhere}
#nursing-cosmos .nc-example{padding-left:12px;border-left:2px solid #61527e}
#nursing-cosmos .nc-neighbors{display:flex;flex-direction:column;gap:7px}
#nursing-cosmos .nc-neighbor{border:1px solid #303b58;background:#151e34;border-radius:9px;padding:10px 11px;text-align:left;display:block;width:100%}
#nursing-cosmos .nc-neighbor:hover{border-color:#8877b2;background:#1d2742}
#nursing-cosmos .nc-neighbor .nc-neighbor-name{display:flex;justify-content:space-between;gap:6px;font-size:13px;color:#eee8ff}
#nursing-cosmos .nc-neighbor small{display:block;font-size:11px;line-height:1.55;color:#aebad6;margin-top:3px}
#nursing-cosmos .nc-go-galaxy{margin-top:22px;width:100%;padding:10px 9px;border:1px solid #655780;border-radius:9px;background:#28243d;color:#e8ddff;font-size:12px}
#nursing-cosmos .nc-footer{padding:10px 24px;background:#0b1120;border-top:1px solid #232b41;font-size:11px;color:#9ba8c4;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap}
#nursing-cosmos [hidden]{display:none!important}
#nursing-cosmos .nc-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media(max-width:780px){#nursing-cosmos .nc-main{grid-template-columns:minmax(0,1fr) 236px}#nursing-cosmos .nc-search-note{display:none}#nursing-cosmos .nc-detail{padding:22px 17px}#nursing-cosmos .nc-map{height:650px}#nursing-cosmos .nc-galaxy{width:155px}#nursing-cosmos .nc-galaxy-name{font-size:14px}#nursing-cosmos .nc-map-label{display:none}#nursing-cosmos .nc-top-meta&gt;span:first-child{display:none}}
@media(max-width:640px){#nursing-cosmos{border-radius:12px}#nursing-cosmos .nc-top{padding:17px 16px;flex-wrap:wrap}#nursing-cosmos .nc-top-meta{font-size:11px}#nursing-cosmos .nc-brand-title{font-size:19px}#nursing-cosmos .nc-brand-sub{font-size:10px;letter-spacing:1px}#nursing-cosmos .nc-search-zone{padding:14px 14px 6px}#nursing-cosmos .nc-search-form{padding:9px 10px;gap:8px}#nursing-cosmos .nc-search-form input{font-size:16px}#nursing-cosmos .nc-search-submit{padding:6px 10px;font-size:13px}#nursing-cosmos .nc-main{display:block}#nursing-cosmos .nc-map{height:580px}#nursing-cosmos .nc-map-bar{left:15px;right:15px;top:14px}#nursing-cosmos .nc-map-bottom{padding:12px 15px}#nursing-cosmos .nc-detail{border-left:0;border-top:1px solid var(--nc-border);padding:23px 22px}#nursing-cosmos .nc-detail h2{font-size:23px}#nursing-cosmos .nc-detail-section{margin-top:18px}#nursing-cosmos .nc-detail p{font-size:14px}#nursing-cosmos .nc-footer{padding:11px 16px}#nursing-cosmos .nc-map-bottom&gt;span{display:none}#nursing-cosmos .nc-neighbors{display:grid;grid-template-columns:repeat(auto-fit,minmax(155px,1fr))}#nursing-cosmos .nc-go-galaxy{max-width:330px}#nursing-cosmos .nc-prototype{padding:3px 7px}}
@media(max-width:380px){#nursing-cosmos .nc-top-meta{display:none}#nursing-cosmos .nc-galaxy{width:144px}#nursing-cosmos .nc-galaxy-name{font-size:13px}#nursing-cosmos .nc-galaxy-sub{font-size:11px}}
&lt;/style&gt;
&lt;header class=&quot;nc-top&quot;&gt;
&lt;div class=&quot;nc-brand&quot;&gt;&lt;span class=&quot;nc-orbit-logo&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;div&gt;&lt;div class=&quot;nc-brand-title&quot;&gt;개념 우주&lt;/div&gt;&lt;div class=&quot;nc-brand-sub&quot;&gt;COMMUNITY NURSING ATLAS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;nc-top-meta&quot;&gt;&lt;span&gt;지역사회간호학&lt;/span&gt;&lt;span class=&quot;nc-prototype&quot;&gt;와이어프레임&lt;/span&gt;&lt;/div&gt;
&lt;/header&gt;
&lt;div class=&quot;nc-search-zone&quot;&gt;
&lt;div class=&quot;nc-search-area&quot;&gt;
&lt;form class=&quot;nc-search-form&quot; role=&quot;search&quot;&gt;&lt;i data-lucide=&quot;search&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;label class=&quot;nc-sr&quot; for=&quot;nc-query&quot;&gt;용어 또는 설명에 들어 있는 단어 검색&lt;/label&gt;&lt;input id=&quot;nc-query&quot; type=&quot;search&quot; placeholder=&quot;용어·설명 속 단어로 찾기&quot; autocomplete=&quot;off&quot; aria-controls=&quot;nc-results&quot;&gt;&lt;button type=&quot;submit&quot; class=&quot;nc-search-submit cursor-interaction&quot;&gt;검색&lt;/button&gt;&lt;/form&gt;
&lt;div class=&quot;nc-results&quot; id=&quot;nc-results&quot; hidden&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;nc-search-note&quot;&gt;단어가 떠오르지 않으면&lt;br&gt;아래 은하에서 주제를 골라 보세요.&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;nc-main&quot;&gt;
&lt;section class=&quot;nc-map-wrap&quot; aria-label=&quot;주제별 개념 우주 지도&quot;&gt;
&lt;div class=&quot;nc-map-bar&quot;&gt;&lt;nav class=&quot;nc-breadcrumb&quot; aria-label=&quot;현재 위치&quot;&gt;&lt;button type=&quot;button&quot; class=&quot;nc-home cursor-interaction&quot;&gt;전체 우주&lt;/button&gt;&lt;span class=&quot;nc-crumb-current&quot;&gt;&lt;/span&gt;&lt;/nav&gt;&lt;span class=&quot;nc-map-label&quot;&gt;EXPLORE THE CONNECTIONS&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;nc-map&quot;&gt;
&lt;div class=&quot;nc-starfield&quot; aria-hidden=&quot;true&quot;&gt;&lt;/div&gt;
&lt;svg class=&quot;nc-svg&quot; aria-hidden=&quot;true&quot;&gt;&lt;/svg&gt;
&lt;div class=&quot;nc-objects&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;nc-map-bottom&quot;&gt;&lt;div class=&quot;nc-legend&quot;&gt;&lt;span&gt;&lt;i class=&quot;nc-legend-planet&quot;&gt;&lt;/i&gt;은하 · 주제&lt;/span&gt;&lt;span&gt;&lt;i class=&quot;nc-legend-dot&quot;&gt;&lt;/i&gt;별 · 용어&lt;/span&gt;&lt;span&gt;&lt;i class=&quot;nc-legend-line&quot;&gt;&lt;/i&gt;선 · 개념 관계&lt;/span&gt;&lt;/div&gt;&lt;span class=&quot;nc-map-hint&quot;&gt;은하를 누르면 용어가 펼쳐집니다&lt;/span&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;aside class=&quot;nc-detail&quot; aria-label=&quot;선택한 용어 설명&quot;&gt;&lt;/aside&gt;
&lt;/div&gt;
&lt;footer class=&quot;nc-footer&quot;&gt;&lt;span&gt;화면 설계용 예시 · 용어와 관계는 교재 확인 후 확정&lt;/span&gt;&lt;span&gt;5개 주제 · 25개 예시 용어&lt;/span&gt;&lt;/footer&gt;
&lt;div class=&quot;nc-sr nc-announcement&quot; aria-live=&quot;polite&quot;&gt;&lt;/div&gt;
&lt;script&gt;
(function(){
const root=document.getElementById(&#x27;nursing-cosmos&#x27;);
const $=s=&gt;root.querySelector(s);
const groups=[
{id:&#x27;promotion&#x27;,name:&#x27;건강증진&#x27;,sub:&#x27;건강하게 살아갈 힘 기르기&#x27;,color:&#x27;#9de0cc&#x27;,pos:[.24,.21]},
{id:&#x27;epidemiology&#x27;,name:&#x27;역학과 건강지표&#x27;,sub:&#x27;질병은 얼마나, 누구에게 생길까&#x27;,color:&#x27;#93c8f3&#x27;,pos:[.76,.22]},
{id:&#x27;process&#x27;,name:&#x27;지역사회 간호과정&#x27;,sub:&#x27;문제를 찾고, 해결하고, 돌아보기&#x27;,color:&#x27;#c4a5ff&#x27;,pos:[.48,.49]},
{id:&#x27;family&#x27;,name:&#x27;가족·방문간호&#x27;,sub:&#x27;생활하는 곳에서 돌봄 이어가기&#x27;,color:&#x27;#e5bd8a&#x27;,pos:[.23,.78]},
{id:&#x27;system&#x27;,name:&#x27;보건의료체계&#x27;,sub:&#x27;필요한 서비스와 자원 연결하기&#x27;,color:&#x27;#e5a7c5&#x27;,pos:[.78,.78]}
];
const terms=[
[&#x27;h1&#x27;,&#x27;promotion&#x27;,&#x27;건강증진&#x27;,&#x27;사람들이 자신의 건강을 더 잘 관리하고 개선하도록 돕는 과정입니다.&#x27;,&#x27;걷기 좋은 환경을 만들고, 주민이 함께 건강 활동을 계획합니다.&#x27;,&#x27;건강 향상&#x27;],
[&#x27;h2&#x27;,&#x27;promotion&#x27;,&#x27;보건교육&#x27;,&#x27;건강에 관한 지식과 실천 방법을 배우도록 돕는 활동입니다.&#x27;,&#x27;주민들과 식품의 영양표시를 읽고 활용하는 방법을 연습합니다.&#x27;,&#x27;건강교육&#x27;],
[&#x27;h3&#x27;,&#x27;promotion&#x27;,&#x27;건강행태&#x27;,&#x27;건강과 관련된 일상적인 행동을 뜻합니다.&#x27;,&#x27;걷기, 식사, 수면 등 일상에서 반복하는 행동을 살펴봅니다.&#x27;,&#x27;건강행동 생활습관&#x27;],
[&#x27;h4&#x27;,&#x27;promotion&#x27;,&#x27;주민참여&#x27;,&#x27;주민이 지역의 문제를 파악하고 해결하는 과정에 함께하는 것입니다.&#x27;,&#x27;주민이 마을 건강사업의 주제와 운영 방법을 함께 결정합니다.&#x27;,&#x27;지역주민 참여&#x27;],
[&#x27;h5&#x27;,&#x27;promotion&#x27;,&#x27;역량강화&#x27;,&#x27;자신과 공동체의 건강에 영향을 주는 문제를 다룰 힘을 키우는 과정입니다.&#x27;,&#x27;주민 모임이 필요한 정보를 찾고 마을의 개선안을 제안합니다.&#x27;,&#x27;임파워먼트 empowerment&#x27;],
[&#x27;p1&#x27;,&#x27;process&#x27;,&#x27;지역사회 사정&#x27;,&#x27;지역의 건강 상태와 생활환경, 이용할 수 있는 자원을 살펴보는 과정입니다.&#x27;,&#x27;지역 통계와 주민의 이야기를 모아 어떤 건강 문제가 있는지 알아봅니다.&#x27;,&#x27;지역사회 건강사정 자료수집&#x27;],
[&#x27;p2&#x27;,&#x27;process&#x27;,&#x27;지역사회 간호진단&#x27;,&#x27;사정한 자료를 바탕으로 간호가 필요한 지역사회의 문제를 정리하는 것입니다.&#x27;,&#x27;수집한 자료를 비교하고, 지역에서 해결해야 할 건강 문제를 구체화합니다.&#x27;,&#x27;지역사회 진단 건강문제&#x27;],
[&#x27;p3&#x27;,&#x27;process&#x27;,&#x27;우선순위 설정&#x27;,&#x27;여러 문제 가운데 무엇을 먼저 다룰지 정하는 과정입니다.&#x27;,&#x27;문제의 크기와 심각성, 해결 가능성 등을 살펴 사업의 순서를 정합니다.&#x27;,&#x27;우선순위 무엇부터 먼저&#x27;],
[&#x27;p4&#x27;,&#x27;process&#x27;,&#x27;계획&#x27;,&#x27;목표를 정하고, 이를 달성하기 위한 활동과 자원 등을 구체화하는 단계입니다.&#x27;,&#x27;사업의 대상, 활동, 일정, 담당자와 평가 방법을 정합니다.&#x27;,&#x27;간호계획 사업계획 목표&#x27;],
[&#x27;p5&#x27;,&#x27;process&#x27;,&#x27;수행&#x27;,&#x27;계획한 간호 활동이나 사업을 실제로 실행하는 단계입니다.&#x27;,&#x27;계획에 따라 주민 교육을 운영하고 참여 상황을 기록합니다.&#x27;,&#x27;간호수행 실행 사업운영&#x27;],
[&#x27;p6&#x27;,&#x27;process&#x27;,&#x27;평가&#x27;,&#x27;사업이 어떻게 진행되었고 어떤 변화가 있었는지 살펴 판단하는 과정입니다.&#x27;,&#x27;계획한 활동이 진행되었는지와 목표한 변화가 나타났는지 확인합니다.&#x27;,&#x27;간호평가 사업평가&#x27;],
[&#x27;e1&#x27;,&#x27;epidemiology&#x27;,&#x27;역학&#x27;,&#x27;인구집단에서 건강과 질병이 어떻게 분포하고 무엇과 관련되는지 연구하는 분야입니다.&#x27;,&#x27;지역이나 연령에 따라 건강 문제의 분포가 다른지 살펴봅니다.&#x27;,&#x27;epidemiology&#x27;],
[&#x27;e2&#x27;,&#x27;epidemiology&#x27;,&#x27;발생률&#x27;,&#x27;일정 기간에 질병이 새롭게 발생하는 빈도를 나타내는 지표입니다.&#x27;,&#x27;관찰 기간 중 새롭게 질병이 발생한 사람을 살펴봅니다.&#x27;,&#x27;새 환자 신규 발생 incidence&#x27;],
[&#x27;e3&#x27;,&#x27;epidemiology&#x27;,&#x27;유병률&#x27;,&#x27;특정 시점이나 기간에 질병을 가진 사람의 비율을 나타내는 지표입니다.&#x27;,&#x27;현재 지역주민 가운데 해당 질병을 가진 사람이 얼마나 되는지 살펴봅니다.&#x27;,&#x27;기존 환자 prevalence&#x27;],
[&#x27;e4&#x27;,&#x27;epidemiology&#x27;,&#x27;위험요인&#x27;,&#x27;질병이나 건강 문제의 발생 가능성과 관련된 특성이나 노출입니다.&#x27;,&#x27;어떤 생활환경이나 행동이 건강 문제와 관련되는지 살펴봅니다.&#x27;,&#x27;위험인자 risk factor&#x27;],
[&#x27;e5&#x27;,&#x27;epidemiology&#x27;,&#x27;건강지표&#x27;,&#x27;인구집단의 건강 상태 등을 수치로 나타내는 지표입니다.&#x27;,&#x27;지역별 건강 수준을 비교하거나 시간에 따른 변화를 살펴봅니다.&#x27;,&#x27;건강수준 통계&#x27;],
[&#x27;f1&#x27;,&#x27;family&#x27;,&#x27;가족간호&#x27;,&#x27;가족의 건강 문제와 강점을 살펴 건강을 유지하고 증진하도록 돕는 간호입니다.&#x27;,&#x27;가족 구성원의 역할과 관계, 건강관리 방법을 함께 살펴봅니다.&#x27;,&#x27;가족 건강&#x27;],
[&#x27;f2&#x27;,&#x27;family&#x27;,&#x27;방문간호&#x27;,&#x27;대상자가 생활하는 곳을 방문하여 제공하는 간호입니다.&#x27;,&#x27;가정을 방문하여 건강 상태와 실제 생활환경을 함께 살펴봅니다.&#x27;,&#x27;가정방문 방문건강관리&#x27;],
[&#x27;f3&#x27;,&#x27;family&#x27;,&#x27;사례관리&#x27;,&#x27;복합적인 요구가 있는 대상자에게 필요한 서비스가 연결되도록 조정하는 과정입니다.&#x27;,&#x27;건강관리와 생활지원이 함께 필요한 대상자의 서비스 이용을 조정합니다.&#x27;,&#x27;케이스매니지먼트&#x27;],
[&#x27;f4&#x27;,&#x27;family&#x27;,&#x27;돌봄의 연속성&#x27;,&#x27;시간이나 서비스 제공 장소가 바뀌어도 돌봄이 이어지는 것을 뜻합니다.&#x27;,&#x27;퇴원 후에도 지역에서 필요한 건강관리를 받을 수 있도록 연결합니다.&#x27;,&#x27;계속 돌봄 지속적 관리&#x27;],
[&#x27;s1&#x27;,&#x27;system&#x27;,&#x27;보건의료체계&#x27;,&#x27;사람들에게 보건의료를 제공하기 위한 조직, 인력, 자원과 제도의 체계입니다.&#x27;,&#x27;지역의 기관들이 어떤 역할로 건강관리를 제공하는지 살펴봅니다.&#x27;,&#x27;보건의료시스템&#x27;],
[&#x27;s2&#x27;,&#x27;system&#x27;,&#x27;일차보건의료&#x27;,&#x27;주민이 가까운 생활권에서 필요한 기본적인 건강관리를 받을 수 있도록 하는 접근입니다.&#x27;,&#x27;지역의 건강 요구에 대응하는 접근성 높은 서비스를 생각해 봅니다.&#x27;,&#x27;primary health care PHC&#x27;],
[&#x27;s3&#x27;,&#x27;system&#x27;,&#x27;보건소&#x27;,&#x27;지역주민의 건강증진과 질병예방 등 공공보건 기능을 수행하는 기관입니다.&#x27;,&#x27;지역의 건강사업을 운영하고 주민에게 필요한 보건서비스를 제공합니다.&#x27;,&#x27;지역보건기관&#x27;],
[&#x27;s4&#x27;,&#x27;system&#x27;,&#x27;의뢰&#x27;,&#x27;대상자의 요구에 맞는 전문 인력이나 기관으로 연결하는 것입니다.&#x27;,&#x27;추가적인 서비스가 필요한 대상자를 적절한 기관으로 연결합니다.&#x27;,&#x27;기관 의뢰 referral&#x27;],
[&#x27;s5&#x27;,&#x27;system&#x27;,&#x27;자원 연계&#x27;,&#x27;대상자나 지역에 필요한 자원과 서비스를 찾아 연결하는 것입니다.&#x27;,&#x27;건강관리 외에 필요한 생활지원 자원도 찾아 연결합니다.&#x27;,&#x27;서비스 연결 지역자원&#x27;]
].map(([id,group,name,desc,example,aliases])=&gt;({id,group,name,desc,example,aliases}));
const edges=[
[&#x27;p1&#x27;,&#x27;p2&#x27;,&#x27;사정 자료를 바탕으로 문제를 정리&#x27;,&#x27;문제 정리&#x27;],[&#x27;p2&#x27;,&#x27;p3&#x27;,&#x27;정리한 문제 중 먼저 다룰 문제를 선택&#x27;,&#x27;순서 결정&#x27;],[&#x27;p3&#x27;,&#x27;p4&#x27;,&#x27;선택한 문제에 대한 활동을 계획&#x27;,&#x27;계획 수립&#x27;],[&#x27;p4&#x27;,&#x27;p5&#x27;,&#x27;계획한 활동을 실행&#x27;,&#x27;실행&#x27;],[&#x27;p5&#x27;,&#x27;p6&#x27;,&#x27;수행 과정과 결과를 살펴봄&#x27;,&#x27;평가&#x27;],[&#x27;p6&#x27;,&#x27;p1&#x27;,&#x27;평가 결과를 다음 사정에 반영&#x27;,&#x27;다시 사정&#x27;],
[&#x27;e5&#x27;,&#x27;p1&#x27;,&#x27;건강지표를 사정 자료로 활용&#x27;,&#x27;사정 자료&#x27;],[&#x27;h1&#x27;,&#x27;h2&#x27;,&#x27;보건교육은 건강증진에 활용되는 방법&#x27;,&#x27;활용 방법&#x27;],[&#x27;h2&#x27;,&#x27;h3&#x27;,&#x27;건강행태에 관한 학습과 실천을 지원&#x27;,&#x27;실천 지원&#x27;],[&#x27;h4&#x27;,&#x27;h1&#x27;,&#x27;주민참여를 통해 건강증진을 추진&#x27;,&#x27;함께 추진&#x27;],[&#x27;h4&#x27;,&#x27;h5&#x27;,&#x27;참여를 통해 공동체의 역량을 키움&#x27;,&#x27;역량 강화&#x27;],
[&#x27;e1&#x27;,&#x27;e5&#x27;,&#x27;역학에서 건강 상태를 나타내는 지표를 활용&#x27;,&#x27;지표 활용&#x27;],[&#x27;e5&#x27;,&#x27;e2&#x27;,&#x27;발생률은 건강지표의 한 예&#x27;,&#x27;지표의 예&#x27;],[&#x27;e5&#x27;,&#x27;e3&#x27;,&#x27;유병률은 건강지표의 한 예&#x27;,&#x27;지표의 예&#x27;],[&#x27;e2&#x27;,&#x27;e3&#x27;,&#x27;새로운 발생과 질병 보유를 구분해 이해&#x27;,&#x27;구분할 개념&#x27;],[&#x27;e1&#x27;,&#x27;e4&#x27;,&#x27;역학에서 건강 문제와 위험요인의 관련성을 탐구&#x27;,&#x27;관련성 탐구&#x27;],
[&#x27;f1&#x27;,&#x27;f2&#x27;,&#x27;가족이 생활하는 곳에서 간호 제공&#x27;,&#x27;방문하여 돌봄&#x27;],[&#x27;f2&#x27;,&#x27;f3&#x27;,&#x27;방문 중 파악한 복합적인 요구의 관리를 연결&#x27;,&#x27;요구에 대응&#x27;],[&#x27;f3&#x27;,&#x27;f4&#x27;,&#x27;서비스 조정을 통해 돌봄이 이어지도록 지원&#x27;,&#x27;연속성 지원&#x27;],[&#x27;f3&#x27;,&#x27;s5&#x27;,&#x27;필요한 자원과 서비스를 찾아 연결&#x27;,&#x27;자원 연결&#x27;],
[&#x27;s1&#x27;,&#x27;s2&#x27;,&#x27;보건의료체계 안에서 일차보건의료를 구현&#x27;,&#x27;체계 안 접근&#x27;],[&#x27;s1&#x27;,&#x27;s3&#x27;,&#x27;보건소는 지역 보건의료체계의 구성 기관&#x27;,&#x27;구성 기관&#x27;],[&#x27;s3&#x27;,&#x27;s4&#x27;,&#x27;필요에 따라 다른 전문 기관으로 의뢰&#x27;,&#x27;기관 간 의뢰&#x27;],[&#x27;s4&#x27;,&#x27;s5&#x27;,&#x27;요구에 맞는 서비스와 자원 연결&#x27;,&#x27;연결 방법&#x27;]
];
const byId=Object.fromEntries(terms.map(t=&gt;[t.id,t]));
const byGroup=Object.fromEntries(groups.map(g=&gt;[g.id,g]));
const map=$(&#x27;.nc-map&#x27;),objects=$(&#x27;.nc-objects&#x27;),svg=$(&#x27;.nc-svg&#x27;),detail=$(&#x27;.nc-detail&#x27;),query=$(&#x27;#nc-query&#x27;),results=$(&#x27;.nc-results&#x27;);
let view=&#x27;universe&#x27;,selected=&#x27;p1&#x27;;
const ns=&#x27;http://www.w3.org/2000/svg&#x27;;
const esc=s=&gt;String(s).replace(/[&amp;&lt;&gt;&quot;&#x27;]/g,c=&gt;({&#x27;&amp;&#x27;:&#x27;&amp;amp;&#x27;,&#x27;&lt;&#x27;:&#x27;&amp;lt;&#x27;,&#x27;&gt;&#x27;:&#x27;&amp;gt;&#x27;,&#x27;&quot;&#x27;:&#x27;&amp;quot;&#x27;,&quot;&#x27;&quot;:&#x27;&amp;#39;&#x27;}[c]));
function el(tag,attrs){const x=document.createElementNS(ns,tag);Object.entries(attrs).forEach(([k,v])=&gt;x.setAttribute(k,v));return x;}
function announce(text){$(&#x27;.nc-announcement&#x27;).textContent=text;}
function stars(){let seed=17;const next=()=&gt;{seed=(seed*16807)%2147483647;return seed/2147483647;};for(let i=0;i&lt;155;i++){const dot=document.createElement(&#x27;i&#x27;);dot.className=&#x27;nc-spark&#x27;+(i%16===0?&#x27; big&#x27;:&#x27;&#x27;);dot.style.left=(next()*100)+&#x27;%&#x27;;dot.style.top=(next()*100)+&#x27;%&#x27;;dot.style.opacity=.12+next()*.42;$(&#x27;.nc-starfield&#x27;).appendChild(dot);}}
function star(t,x,y,small){const g=byGroup[t.group];const b=document.createElement(&#x27;button&#x27;);b.type=&#x27;button&#x27;;b.className=&#x27;nc-star-node cursor-interaction&#x27;+(selected===t.id?&#x27; is-selected&#x27;:&#x27;&#x27;);b.style.left=x+&#x27;px&#x27;;b.style.top=y+&#x27;px&#x27;;b.style.setProperty(&#x27;--nc-glow&#x27;,g.color);b.dataset.term=t.id;b.setAttribute(&#x27;aria-label&#x27;,t.name+&#x27; · &#x27;+g.name);b.setAttribute(&#x27;aria-pressed&#x27;,String(selected===t.id));b.innerHTML=&#x27;&lt;span class=&quot;nc-dot&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nc-node-name&quot;&gt;&#x27;+esc(t.name)+&#x27;&lt;/span&gt;&#x27;;b.addEventListener(&#x27;click&#x27;,()=&gt;select(t.id,view===&#x27;universe&#x27;));objects.appendChild(b);return b;}
function line(a,b,edge,active){const path=el(&#x27;path&#x27;,{d:`M ${a[0]} ${a[1]-10} Q ${(a[0]+b[0])/2+12} ${(a[1]+b[1])/2-22} ${b[0]} ${b[1]-10}`,fill:&#x27;none&#x27;,stroke:active?&#x27;#bfa1f5&#x27;:&#x27;#4b4e6c&#x27;,&#x27;stroke-width&#x27;:active?&#x27;1.4&#x27;:&#x27;1&#x27;,opacity:active?&#x27;.85&#x27;:&#x27;.45&#x27;});svg.appendChild(path);if(active&amp;&amp;view!==&#x27;universe&#x27;){const x=(a[0]+b[0])/2+6,y=(a[1]+b[1])/2-22;const text=el(&#x27;text&#x27;,{x,y,fill:&#x27;#c5b4e5&#x27;,&#x27;font-size&#x27;:&#x27;11&#x27;,&#x27;text-anchor&#x27;:&#x27;middle&#x27;,&#x27;paint-order&#x27;:&#x27;stroke&#x27;,stroke:&#x27;#0b1020&#x27;,&#x27;stroke-width&#x27;:&#x27;5&#x27;,&#x27;stroke-linejoin&#x27;:&#x27;round&#x27;});text.textContent=edge[3];svg.appendChild(text);}}
function draw(){const w=map.clientWidth,h=map.clientHeight,narrow=w&lt;620;svg.setAttribute(&#x27;viewBox&#x27;,`0 0 ${w} ${h}`);objects.replaceChildren();svg.replaceChildren();map.classList.toggle(&#x27;nc-focus-mode&#x27;,view!==&#x27;universe&#x27;);$(&#x27;.nc-crumb-current&#x27;).textContent=view===&#x27;universe&#x27;?&#x27;&#x27;:(&#x27; / &#x27;+byGroup[view].name);$(&#x27;.nc-map-hint&#x27;).textContent=view===&#x27;universe&#x27;?&#x27;은하를 누르면 용어가 펼쳐집니다&#x27;:&#x27;별을 누르면 설명과 관계를 볼 수 있습니다&#x27;;
if(view===&#x27;universe&#x27;){
groups.forEach((g,i)=&gt;{const pos=narrow?[[.25,.20],[.76,.25],[.49,.47],[.24,.72],[.75,.80]][i]:g.pos;const x=w*pos[0],y=h*pos[1];const e=el(&#x27;ellipse&#x27;,{cx:x,cy:y-15,rx:narrow?65:88,ry:narrow?34:43,fill:&#x27;none&#x27;,stroke:g.color,&#x27;stroke-width&#x27;:&#x27;.6&#x27;,opacity:&#x27;.20&#x27;,transform:`rotate(-25 ${x} ${y-15})`});svg.appendChild(e);const b=document.createElement(&#x27;button&#x27;);b.type=&#x27;button&#x27;;b.className=&#x27;nc-galaxy cursor-interaction&#x27;;b.style.left=x+&#x27;px&#x27;;b.style.top=y+&#x27;px&#x27;;b.style.setProperty(&#x27;--nc-glow&#x27;,g.color);const list=terms.filter(t=&gt;t.group===g.id);b.setAttribute(&#x27;aria-label&#x27;,g.name+&#x27; 은하 · &#x27;+list.length+&#x27;개 용어 펼치기&#x27;);b.innerHTML=&#x27;&lt;span class=&quot;nc-galaxy-name&quot;&gt;&#x27;+g.name+&#x27;&lt;/span&gt;&lt;span class=&quot;nc-galaxy-sub&quot;&gt;&#x27;+g.sub+&#x27;&lt;/span&gt;&#x27;;b.addEventListener(&#x27;click&#x27;,()=&gt;{view=g.id;if(byId[selected].group!==g.id)selected=list[0].id;draw();renderDetail();announce(g.name+&#x27; 은하, &#x27;+list.length+&#x27;개 용어&#x27;);});objects.appendChild(b);
if(!narrow){const a=list[0],c=list[1];const yshift=(i===3||i===4)?-79:74;const pa=[x-46,y+yshift],pc=[x+68,y+yshift+19];star(a,pa[0],pa[1],true);star(c,pc[0],pc[1],true);const edge=edges.find(e=&gt;e[0]===a.id&amp;&amp;e[1]===c.id);if(edge)line(pa,pc,edge,selected===a.id||selected===c.id);}else if(g.id===&#x27;process&#x27;){star(byId.p1,x,y+77,true);}
});
}else{
const g=byGroup[view],list=terms.filter(t=&gt;t.group===view),cx=w*.5,cy=h*.50,rx=Math.max(80,w*.33),ry=h*.31;
[1,.78].forEach((scale,i)=&gt;svg.appendChild(el(&#x27;ellipse&#x27;,{cx,cy,rx:rx*scale,ry:ry*scale,fill:&#x27;none&#x27;,stroke:g.color,&#x27;stroke-width&#x27;:&#x27;.7&#x27;,opacity:i?&#x27;.09&#x27;:&#x27;.17&#x27;,transform:`rotate(-12 ${cx} ${cy})`})));
const center=document.createElement(&#x27;div&#x27;);center.className=&#x27;nc-focus-title&#x27;;center.style.left=cx+&#x27;px&#x27;;center.style.top=cy+&#x27;px&#x27;;center.innerHTML=&#x27;&lt;span class=&quot;nc-planet&quot; aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;&lt;strong&gt;&#x27;+g.name+&#x27;&lt;/strong&gt;&lt;small&gt;&#x27;+list.length+&#x27;개의 개념으로 이어지는 은하&lt;/small&gt;&#x27;;objects.appendChild(center);
const positions={};list.forEach((t,i)=&gt;{let theta=(-90+i*360/list.length)*Math.PI/180;positions[t.id]=[cx+rx*Math.cos(theta),cy+ry*Math.sin(theta)];});
edges.filter(e=&gt;positions[e[0]]&amp;&amp;positions[e[1]]).forEach(e=&gt;{const active=e[0]===selected||e[1]===selected;line(positions[e[0]],positions[e[1]],e,active);});
list.forEach(t=&gt;star(t,positions[t.id][0],positions[t.id][1],false));
}
}
function select(id,focus){selected=id;if(focus||view!==&#x27;universe&#x27;)view=byId[id].group;draw();renderDetail();announce(byId[id].name+&#x27; 선택됨. &#x27;+byId[id].desc);}
function renderDetail(){const t=byId[selected],g=byGroup[t.group],near=edges.filter(e=&gt;e[0]===selected||e[1]===selected);detail.innerHTML=&#x27;&lt;div class=&quot;nc-detail-kicker&quot;&gt;&lt;span class=&quot;nc-detail-dot&quot;&gt;&lt;/span&gt;지금 살펴보는 별&lt;/div&gt;&lt;div class=&quot;nc-detail-category&quot;&gt;&#x27;+g.name+&#x27;&lt;/div&gt;&lt;h2&gt;&#x27;+t.name+&#x27;&lt;/h2&gt;&lt;div class=&quot;nc-alias&quot;&gt;함께 찾는 말 · &#x27;+esc(t.aliases.split(&#x27; &#x27;).slice(0,2).join(&#x27; &#x27;))+&#x27;&lt;/div&gt;&lt;section class=&quot;nc-detail-section&quot;&gt;&lt;h3&gt;쉬운 뜻&lt;/h3&gt;&lt;p&gt;&#x27;+t.desc+&#x27;&lt;/p&gt;&lt;/section&gt;&lt;section class=&quot;nc-detail-section&quot;&gt;&lt;h3&gt;이런 장면을 떠올려 보세요&lt;/h3&gt;&lt;p class=&quot;nc-example&quot;&gt;&#x27;+t.example+&#x27;&lt;/p&gt;&lt;/section&gt;&lt;section class=&quot;nc-detail-section&quot;&gt;&lt;h3&gt;연결된 개념&lt;/h3&gt;&lt;div class=&quot;nc-neighbors&quot;&gt;&#x27;+near.slice(0,3).map(e=&gt;{const n=byId[e[0]===selected?e[1]:e[0]];return &#x27;&lt;button type=&quot;button&quot; class=&quot;nc-neighbor cursor-interaction&quot; data-neighbor=&quot;&#x27;+n.id+&#x27;&quot;&gt;&lt;span class=&quot;nc-neighbor-name&quot;&gt;&#x27;+n.name+&#x27; &lt;span aria-hidden=&quot;true&quot;&gt;↗&lt;/span&gt;&lt;/span&gt;&lt;small&gt;&#x27;+e[2]+(n.group!==t.group?&#x27; · 다른 은하&#x27;:&#x27;&#x27;)+&#x27;&lt;/small&gt;&lt;/button&gt;&#x27;;}).join(&#x27;&#x27;)+&#x27;&lt;/div&gt;&lt;/section&gt;&lt;button type=&quot;button&quot; class=&quot;nc-go-galaxy cursor-interaction&quot;&gt;&#x27;+(view===&#x27;universe&#x27;?&#x27;이 은하의 용어 펼쳐보기&#x27;:&#x27;전체 우주로 돌아가기&#x27;)+&#x27; &lt;span aria-hidden=&quot;true&quot;&gt;↗&lt;/span&gt;&lt;/button&gt;&#x27;;
detail.querySelectorAll(&#x27;[data-neighbor]&#x27;).forEach(b=&gt;b.addEventListener(&#x27;click&#x27;,()=&gt;select(b.dataset.neighbor,true)));
detail.querySelector(&#x27;.nc-go-galaxy&#x27;).addEventListener(&#x27;click&#x27;,()=&gt;{view=view===&#x27;universe&#x27;?t.group:&#x27;universe&#x27;;draw();renderDetail();announce(view===&#x27;universe&#x27;?&#x27;전체 우주로 이동&#x27;:g.name+&#x27; 은하로 이동&#x27;);});
}
const normalize=s=&gt;s.toLowerCase().replace(/\s/g,&#x27;&#x27;);
function search(){const q=query.value.trim();if(!q){results.hidden=true;return;}const parts=q.toLowerCase().split(/[\s,]+/).filter(Boolean);let hits=terms.map(t=&gt;{const full=normalize(t.name+&#x27; &#x27;+t.aliases+&#x27; &#x27;+t.desc),name=normalize(t.name);let score=0;if(name.includes(normalize(q)))score+=12;parts.forEach(p=&gt;{if(name.includes(p))score+=5;else if(full.includes(p))score+=1;});return {t,score};}).filter(x=&gt;x.score&gt;0).sort((a,b)=&gt;b.score-a.score);results.hidden=false;results.innerHTML=&#x27;&lt;div class=&quot;nc-result-head&quot;&gt;&lt;span&gt;등록된 용어·동의어·설명에서 &#x27;+hits.length+&#x27;개 찾음&lt;/span&gt;&lt;button type=&quot;button&quot; class=&quot;nc-close-search cursor-interaction&quot; aria-label=&quot;검색 결과 닫기&quot;&gt;닫기&lt;/button&gt;&lt;/div&gt;&#x27;+(hits.length?hits.slice(0,6).map(({t})=&gt;&#x27;&lt;button type=&quot;button&quot; class=&quot;nc-result cursor-interaction&quot; data-result=&quot;&#x27;+t.id+&#x27;&quot;&gt;&lt;span&gt;&lt;strong&gt;&#x27;+t.name+&#x27;&lt;/strong&gt;&lt;small&gt;&#x27;+byGroup[t.group].name+&#x27;&lt;/small&gt;&lt;/span&gt;&lt;span class=&quot;nc-result-arrow&quot; aria-hidden=&quot;true&quot;&gt;↗&lt;/span&gt;&lt;/button&gt;&#x27;).join(&#x27;&#x27;):&#x27;&lt;p class=&quot;nc-empty&quot;&gt;일치하는 용어가 없습니다.&lt;br&gt;짧은 단어로 다시 찾거나, 아래 은하에서 주제를 골라 보세요.&lt;/p&gt;&#x27;);results.querySelector(&#x27;.nc-close-search&#x27;).addEventListener(&#x27;click&#x27;,()=&gt;{results.hidden=true;query.focus();});results.querySelectorAll(&#x27;[data-result]&#x27;).forEach(b=&gt;b.addEventListener(&#x27;click&#x27;,()=&gt;{select(b.dataset.result,true);results.hidden=true;query.value=byId[b.dataset.result].name;}));announce(hits.length+&#x27;개의 검색 결과&#x27;);}
$(&#x27;.nc-search-form&#x27;).addEventListener(&#x27;submit&#x27;,e=&gt;{e.preventDefault();search();});query.addEventListener(&#x27;input&#x27;,search);query.addEventListener(&#x27;keydown&#x27;,e=&gt;{if(e.key===&#x27;Escape&#x27;)results.hidden=true;});
root.addEventListener(&#x27;click&#x27;,e=&gt;{if(!e.target.closest(&#x27;.nc-search-area&#x27;))results.hidden=true;});
$(&#x27;.nc-home&#x27;).addEventListener(&#x27;click&#x27;,()=&gt;{view=&#x27;universe&#x27;;results.hidden=true;draw();renderDetail();announce(&#x27;전체 우주로 이동&#x27;);});
stars();draw();renderDetail();new ResizeObserver(()=&gt;draw()).observe(map);
})();
&lt;/script&gt;
&lt;/div&gt;
&lt;script&gt;
(() =&gt; {
if (window.FloatingUIDOM == null) {
return;
}
const { autoUpdate, computePosition, flip, offset, shift, size } =
window.FloatingUIDOM;
const tooltipPlacements = new Set([&quot;top&quot;, &quot;right&quot;, &quot;bottom&quot;, &quot;left&quot;]);
const tooltipId = `codex-visualization-tooltip-${
window.crypto?.randomUUID?.() ?? Date.now().toString(36)
}`;
const tooltipGap = 5;
const hoverDelay = 700;
const skipDelay = 300;
let activeTrigger = null;
let pendingTrigger = null;
let tooltip = null;
let openTimeout = null;
let autoUpdateCleanup = null;
let positionRequest = 0;
let skipDelayUntil = 0;
let lastPointerWasTouch = false;
let pinnedByTouch = false;
const getTrigger = (event) =&gt; {
for (const target of event.composedPath()) {
if (target?.nodeType === Node.ELEMENT_NODE) {
const trigger = target.closest(&quot;[data-tooltip]&quot;);
if (trigger) return trigger;
}
}
return null;
};
const containsTarget = (trigger, target) =&gt;
target?.nodeType != null &amp;&amp; trigger.contains(target);
const getContent = (trigger) =&gt;
trigger.getAttribute(&quot;data-tooltip&quot;)?.trim() ?? &quot;&quot;;
const getPlacement = (trigger) =&gt; {
const placement = trigger.getAttribute(&quot;data-tooltip-placement&quot;);
return tooltipPlacements.has(placement) ? placement : &quot;top&quot;;
};
const getTooltip = () =&gt; {
if (tooltip == null) {
tooltip = document.createElement(&quot;div&quot;);
tooltip.id = tooltipId;
tooltip.className = &quot;tooltip&quot;;
tooltip.setAttribute(&quot;role&quot;, &quot;tooltip&quot;);
}
return tooltip;
};
const setDescribedBy = (trigger, described) =&gt; {
const ids = new Set(
(trigger.getAttribute(&quot;aria-describedby&quot;) ?? &quot;&quot;)
.split(/\s+/)
.filter(Boolean),
);
if (described) {
ids.add(tooltipId);
} else {
ids.delete(tooltipId);
}
if (ids.size === 0) {
trigger.removeAttribute(&quot;aria-describedby&quot;);
} else {
trigger.setAttribute(&quot;aria-describedby&quot;, [...ids].join(&quot; &quot;));
}
};
const clearOpenTimeout = () =&gt; {
if (openTimeout != null) {
window.clearTimeout(openTimeout);
openTimeout = null;
}
pendingTrigger = null;
};
const closeTooltip = (activateSkipDelay = true) =&gt; {
clearOpenTimeout();
pinnedByTouch = false;
positionRequest += 1;
autoUpdateCleanup?.();
autoUpdateCleanup = null;
if (activeTrigger == null) {
return;
}
setDescribedBy(activeTrigger, false);
activeTrigger = null;
tooltip?.remove();
if (activateSkipDelay) {
skipDelayUntil = Date.now() + skipDelay;
}
};
const updatePosition = (trigger, floating) =&gt; {
const request = ++positionRequest;
void computePosition(trigger, floating, {
middleware: [
offset(tooltipGap),
flip({ padding: tooltipGap }),
shift({ padding: tooltipGap }),
size({
padding: tooltipGap,
apply({ availableHeight, availableWidth, elements }) {
elements.floating.style.setProperty(
&quot;--tooltip-available-width&quot;,
`${Math.max(0, availableWidth)}px`,
);
elements.floating.style.setProperty(
&quot;--tooltip-available-height&quot;,
`${Math.max(0, availableHeight)}px`,
);
},
}),
],
placement: getPlacement(trigger),
strategy: &quot;fixed&quot;,
})
.then(({ x, y }) =&gt; {
if (activeTrigger !== trigger || request !== positionRequest) {
return;
}
const scale = window.devicePixelRatio || 1;
floating.style.transform = `translate(${Math.round(x * scale) / scale}px, ${Math.round(y * scale) / scale}px)`;
floating.style.visibility = &quot;visible&quot;;
})
.catch(() =&gt; {
if (activeTrigger === trigger &amp;&amp; request === positionRequest) {
closeTooltip(false);
}
});
};
const openTooltip = (trigger) =&gt; {
const content = getContent(trigger);
if (!trigger.isConnected || content.length === 0) {
return;
}
clearOpenTimeout();
if (activeTrigger === trigger) {
return;
}
closeTooltip();
const floating = getTooltip();
floating.textContent = content;
floating.style.visibility = &quot;hidden&quot;;
floating.style.transform = &quot;translate(0, 0)&quot;;
document.body.appendChild(floating);
activeTrigger = trigger;
setDescribedBy(trigger, true);
autoUpdateCleanup = autoUpdate(trigger, floating, () =&gt; {
updatePosition(trigger, floating);
});
};
const requestOpen = (trigger, immediate = false) =&gt; {
if (
getContent(trigger).length === 0 ||
activeTrigger === trigger ||
pendingTrigger === trigger
) {
return;
}
clearOpenTimeout();
pendingTrigger = trigger;
const delay =
immediate || activeTrigger != null || Date.now() &lt; skipDelayUntil
? 0
: hoverDelay;
if (delay === 0) {
openTooltip(trigger);
return;
}
openTimeout = window.setTimeout(() =&gt; {
openTimeout = null;
pendingTrigger = null;
openTooltip(trigger);
}, delay);
};
const handlePointerOpen = (event) =&gt; {
if (event.pointerType === &quot;touch&quot;) {
return;
}
const trigger = getTrigger(event);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
requestOpen(trigger);
};
const handlePointer = (event) =&gt; {
lastPointerWasTouch =
event.type === &quot;pointerdown&quot; &amp;&amp; event.pointerType === &quot;touch&quot;;
};
const handleTouchClick = (event) =&gt; {
const isTouch = event.pointerType === &quot;touch&quot; || lastPointerWasTouch;
lastPointerWasTouch = false;
if (!isTouch || event.detail === 0) {
return;
}
const trigger = getTrigger(event);
if (
trigger == null ||
!trigger.isConnected ||
getContent(trigger).length === 0 ||
(pinnedByTouch &amp;&amp; activeTrigger === trigger)
) {
if (pinnedByTouch) {
closeTooltip();
}
return;
}
openTooltip(trigger);
pinnedByTouch = activeTrigger === trigger;
window.setTimeout(() =&gt; {
if (activeTrigger === trigger &amp;&amp; !trigger.isConnected) {
closeTooltip(false);
}
}, 0);
};
const handleLeave = (event) =&gt; {
if (pinnedByTouch) {
return;
}
const trigger = getTrigger(event);
if (trigger == null || containsTarget(trigger, event.relatedTarget)) {
return;
}
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const handleFocusIn = (event) =&gt; {
const trigger = getTrigger(event);
if (trigger?.matches(&quot;:focus-visible&quot;)) {
requestOpen(trigger, true);
}
};
const handleKeyDown = (event) =&gt; {
if (event.key === &quot;Escape&quot;) {
closeTooltip();
}
};
const handleContextMenu = (event) =&gt; {
lastPointerWasTouch = false;
const trigger = getTrigger(event);
if (activeTrigger === trigger || pendingTrigger === trigger) {
closeTooltip();
}
};
const handlers = {
pointerover: handlePointerOpen,
pointerdown: handlePointer,
pointercancel: handlePointer,
click: handleTouchClick,
pointerout: handleLeave,
focusin: handleFocusIn,
focusout: handleLeave,
keydown: handleKeyDown,
contextmenu: handleContextMenu,
};
const roots = new Map();
const handledEvents = new WeakSet();
const handleEvent = (event) =&gt; {
if (handledEvents.has(event)) return;
handledEvents.add(event);
handlers[event.type](event);
};
const discoverRoots = (node) =&gt; {
if (node.shadowRoot) listen(node.shadowRoot);
for (const element of node.querySelectorAll(&quot;*&quot;)) {
if (element.shadowRoot) listen(element.shadowRoot);
}
};
const listen = (root) =&gt; {
if (roots.has(root)) return;
for (const type of Object.keys(handlers)) root.addEventListener(type, handleEvent);
const observer = new window.MutationObserver((records) =&gt; {
for (const { addedNodes } of records) {
for (const node of addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) discoverRoots(node);
}
}
for (const [root, cleanup] of roots) {
if (root.host &amp;&amp; !root.host.isConnected) {
cleanup();
roots.delete(root);
}
}
if (activeTrigger &amp;&amp; !activeTrigger.isConnected) closeTooltip(false);
});
observer.observe(root, { childList: true, subtree: true });
roots.set(root, () =&gt; {
observer.disconnect();
for (const type of Object.keys(handlers)) root.removeEventListener(type, handleEvent);
});
discoverRoots(root);
};
const destroy = () =&gt; {
closeTooltip(false);
lastPointerWasTouch = false;
for (const cleanup of roots.values()) cleanup();
roots.clear();
tooltip = null;
};
// Events between children of one shadow root never reach the document.
listen(document);
window.addEventListener(&quot;pagehide&quot;, destroy, { once: true });
})();
&lt;/script&gt;
&lt;script id=&quot;codex-visualization-tabs&quot;&gt;
(() =&gt; {
const selector = &#x27;.nav[role=&quot;tablist&quot;] [role=&quot;tab&quot;]&#x27;;
const disabled = &#x27;:disabled,[aria-disabled=&quot;true&quot;]&#x27;;
const selectTab = (selected) =&gt; {
const panelId = selected.getAttribute(&quot;aria-controls&quot;);
for (const tab of selected.closest(&#x27;[role=&quot;tablist&quot;]&#x27;).querySelectorAll(&#x27;[role=&quot;tab&quot;]&#x27;)) {
const active = tab === selected;
tab.classList.toggle(&quot;active&quot;, active);
tab.setAttribute(&quot;aria-selected&quot;, String(active));
const id = tab.getAttribute(&quot;aria-controls&quot;);
const panel = id &amp;&amp; document.getElementById(id);
if (panel != null) {
panel.hidden = id !== panelId;
if (!panel.hidden &amp;&amp; selected.id) {
panel.setAttribute(&quot;aria-labelledby&quot;, selected.id);
}
}
}
};
const interact = (event) =&gt; {
const tab = event.target?.closest?.(selector);
if (event.defaultPrevented || tab == null || tab.matches(disabled)) {
return;
}
if (event.type === &quot;click&quot;) {
selectTab(tab);
return;
}
const tabs = Array.from(
tab.closest(&#x27;[role=&quot;tablist&quot;]&#x27;).querySelectorAll(&#x27;[role=&quot;tab&quot;]&#x27;),
).filter((candidate) =&gt; !candidate.matches(disabled));
const current = tabs.indexOf(tab);
let next;
if (event.key === &quot;ArrowRight&quot; || event.key === &quot;ArrowDown&quot;) {
next = (current + 1) % tabs.length;
} else if (event.key === &quot;ArrowLeft&quot; || event.key === &quot;ArrowUp&quot;) {
next = (current + tabs.length - 1) % tabs.length;
} else if (event.key === &quot;Home&quot;) {
next = 0;
} else if (event.key === &quot;End&quot;) {
next = tabs.length - 1;
} else {
return;
}
event.preventDefault();
tabs[next].focus();
selectTab(tabs[next]);
};
for (const list of document.querySelectorAll(&#x27;.nav[role=&quot;tablist&quot;]&#x27;)) {
const selected =
list.querySelector(&#x27;[role=&quot;tab&quot;][aria-selected=&quot;true&quot;]&#x27;) ??
list.querySelector(&#x27;[role=&quot;tab&quot;].active&#x27;) ??
list.querySelector(&#x27;[role=&quot;tab&quot;]&#x27;);
if (selected != null) {
selectTab(selected);
}
}
document.addEventListener(&quot;click&quot;, interact);
document.addEventListener(&quot;keydown&quot;, interact);
window.addEventListener(&quot;pagehide&quot;, () =&gt; {
document.removeEventListener(&quot;click&quot;, interact);
document.removeEventListener(&quot;keydown&quot;, interact);
}, { once: true });
})();
&lt;/script&gt;
&lt;script id=&quot;codex-visualization-carousel&quot;&gt;
(() =&gt; {
for (const root of document.querySelectorAll(&quot;.viz-carousel&quot;)) {
const panels = Array.from(
root.querySelectorAll(&quot;:scope &gt; [data-variant]&quot;),
);
if (panels.length === 0) {
continue;
}
root.setAttribute(&quot;role&quot;, &quot;group&quot;);
panels.forEach((panel, index) =&gt; {
panel.hidden = index !== 0;
panel.setAttribute(&quot;role&quot;, &quot;group&quot;);
if (!panel.hasAttribute(&quot;aria-label&quot;)) {
panel.setAttribute(&quot;aria-label&quot;, panel.dataset.variant);
}
});
root.dataset.activeVariant = panels[0].dataset.variant;
if (panels.length === 1) {
continue;
}
const controls = document.createElement(&quot;div&quot;);
controls.className = &quot;viz-carousel-controls&quot;;
// Portable copy of the settings dropdown&#x27;s legacy glyph; no 14px icon-package preset matches it.
// https://github.com/openai/openai/blob/master/codex/codex-apps/webview/src/icons/chevron.svg
controls.innerHTML = `
&lt;button type=&quot;button&quot; class=&quot;viz-carousel-previous cursor-interaction&quot;&gt;&lt;i data-lucide=&quot;chevron-left&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;/button&gt;
&lt;details class=&quot;viz-carousel-picker&quot;&gt;
&lt;summary class=&quot;cursor-interaction&quot;&gt;
&lt;span class=&quot;viz-carousel-count tabular-nums&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;viz-carousel-name&quot;&gt;&lt;/span&gt;
&lt;svg class=&quot;viz-carousel-caret&quot; width=&quot;14&quot; height=&quot;14&quot; viewBox=&quot;0 0 20 21&quot; fill=&quot;none&quot; aria-hidden=&quot;true&quot; focusable=&quot;false&quot;&gt;
&lt;path d=&quot;M15.2793 7.71101C15.539 7.45131 15.961 7.45131 16.2207 7.71101C16.4804 7.97071 16.4804 8.39272 16.2207 8.65242L10.4707 14.4024C10.211 14.6621 9.78902 14.6621 9.52932 14.4024L3.77932 8.65242L3.69436 8.54792C3.52385 8.28979 3.55205 7.93828 3.77932 7.71101C4.00659 7.48374 4.3581 7.45554 4.61623 7.62605L4.72073 7.71101L10 12.9903L15.2793 7.71101Z&quot; fill=&quot;currentColor&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;0.6&quot; /&gt;
&lt;/svg&gt;
&lt;/summary&gt;
&lt;div class=&quot;viz-carousel-options&quot;&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;button type=&quot;button&quot; class=&quot;viz-carousel-next cursor-interaction&quot;&gt;&lt;i data-lucide=&quot;chevron-right&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt;&lt;/button&gt;`;
const previous = controls.querySelector(&quot;.viz-carousel-previous&quot;);
const next = controls.querySelector(&quot;.viz-carousel-next&quot;);
const picker = controls.querySelector(&quot;.viz-carousel-picker&quot;);
const summary = picker.querySelector(&quot;summary&quot;);
const count = controls.querySelector(&quot;.viz-carousel-count&quot;);
const name = controls.querySelector(&quot;.viz-carousel-name&quot;);
const options = controls.querySelector(&quot;.viz-carousel-options&quot;);
const status = document.createElement(&quot;span&quot;);
status.className = &quot;sr-only&quot;;
status.setAttribute(&quot;role&quot;, &quot;status&quot;);
status.setAttribute(&quot;aria-live&quot;, &quot;polite&quot;);
status.setAttribute(&quot;aria-atomic&quot;, &quot;true&quot;);
previous.setAttribute(
&quot;aria-label&quot;,
root.dataset.previousLabel ?? &quot;Previous variant&quot;,
);
next.setAttribute(&quot;aria-label&quot;, root.dataset.nextLabel ?? &quot;Next variant&quot;);
let active = 0;
let mousePointerId = null;
let summaryPointerHandled = false;
summary.addEventListener(&quot;pointerdown&quot;, (event) =&gt; {
summaryPointerHandled =
event.pointerType === &quot;mouse&quot; &amp;&amp; event.button === 0 &amp;&amp; !event.ctrlKey;
if (!summaryPointerHandled) {
return;
}
event.preventDefault();
summary.focus({ preventScroll: true });
picker.open = !picker.open;
mousePointerId = picker.open ? event.pointerId : null;
});
summary.addEventListener(&quot;click&quot;, (event) =&gt; {
if (summaryPointerHandled &amp;&amp; event.detail &gt; 0) {
event.preventDefault();
}
});
const choices = panels.map((panel, index) =&gt; {
const choice = document.createElement(&quot;button&quot;);
choice.type = &quot;button&quot;;
choice.className = &quot;cursor-interaction&quot;;
choice.textContent = panel.dataset.variant;
choice.addEventListener(&quot;click&quot;, () =&gt; {
select(index);
summary.focus();
});
choice.addEventListener(&quot;pointerup&quot;, (event) =&gt; {
if (
picker.open &amp;&amp;
event.pointerType === &quot;mouse&quot; &amp;&amp;
event.button === 0 &amp;&amp;
event.pointerId === mousePointerId
) {
choice.click();
}
});
options.appendChild(choice);
return choice;
});
const select = (index) =&gt; {
const changed = active !== index;
active = index;
panels.forEach((panel, position) =&gt; {
panel.hidden = position !== active;
choices[position].setAttribute(
&quot;aria-current&quot;,
String(position === active),
);
});
root.dataset.activeVariant = panels[active].dataset.variant;
count.textContent = `${active + 1} / ${panels.length}`;
name.textContent = panels[active].dataset.variant;
picker.open = false;
mousePointerId = null;
if (changed) {
status.textContent = `${count.textContent}: ${name.textContent}`;
}
};
previous.addEventListener(&quot;click&quot;, () =&gt;
select((active + panels.length - 1) % panels.length),
);
next.addEventListener(&quot;click&quot;, () =&gt;
select((active + 1) % panels.length),
);
picker.addEventListener(&quot;keydown&quot;, (event) =&gt; {
if (event.key === &quot;Escape&quot; &amp;&amp; picker.open) {
event.preventDefault();
picker.open = false;
mousePointerId = null;
summary.focus();
return;
}
if (event.altKey || event.ctrlKey || event.metaKey) {
return;
}
const index = choices.indexOf(document.activeElement);
let nextIndex;
if (event.key === &quot;ArrowDown&quot;) {
nextIndex = index &lt; 0 ? active : (index + 1) % choices.length;
} else if (event.key === &quot;ArrowUp&quot;) {
nextIndex = index &lt; 0 ? active : (index + choices.length - 1) % choices.length;
} else if (picker.open &amp;&amp; event.key === &quot;Home&quot;) {
nextIndex = 0;
} else if (picker.open &amp;&amp; event.key === &quot;End&quot;) {
nextIndex = choices.length - 1;
} else {
return;
}
event.preventDefault();
picker.open = true;
choices[nextIndex].focus();
});
picker.addEventListener(&quot;focusout&quot;, (event) =&gt; {
if (!picker.contains(event.relatedTarget)) {
picker.open = false;
mousePointerId = null;
}
});
const dismiss = (event) =&gt; {
if (!summary.contains(event.target)) {
mousePointerId = null;
}
if (!picker.contains(event.target)) {
picker.open = false;
}
};
const endPointerSelection = () =&gt; {
mousePointerId = null;
};
const sizePicker = () =&gt; {
root.style.setProperty(
&quot;--viz-carousel-picker-height&quot;,
`${picker.open ? options.offsetHeight : 0}px`,
);
};
picker.addEventListener(&quot;toggle&quot;, sizePicker);
window.addEventListener(&quot;resize&quot;, sizePicker);
document.addEventListener(&quot;pointerdown&quot;, dismiss);
document.addEventListener(&quot;pointerup&quot;, endPointerSelection);
document.addEventListener(&quot;pointercancel&quot;, endPointerSelection);
window.addEventListener(&quot;blur&quot;, endPointerSelection);
window.addEventListener(
&quot;pagehide&quot;,
() =&gt; {
document.removeEventListener(&quot;pointerdown&quot;, dismiss);
document.removeEventListener(&quot;pointerup&quot;, endPointerSelection);
document.removeEventListener(&quot;pointercancel&quot;, endPointerSelection);
window.removeEventListener(&quot;blur&quot;, endPointerSelection);
window.removeEventListener(&quot;resize&quot;, sizePicker);
},
{ once: true },
);
root.append(controls, status);
select(0);
}
})();
&lt;/script&gt;
&lt;script&gt;
(() =&gt; {
const initialize = () =&gt; {
globalThis.lucide?.createIcons({ attrs: { width: 16, height: 16 } });
};
if (globalThis.lucide != null) {
initialize();
return;
}
document
.getElementById(&quot;codex-visualization-lucide&quot;)
?.addEventListener(&quot;load&quot;, initialize, { once: true });
})();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
"></iframe>
<script>(() => {
const frame = document.getElementById("codex-visualization");
if (!(frame instanceof HTMLIFrameElement)) {
return;
}
let viewportHeight = globalThis.innerHeight;
const storageKey = `codex:visualization-widget-state-v2:${JSON.stringify([
globalThis.location.pathname,
globalThis.location.search,
])}`;
const parseState = (serializedState) => {
if (
typeof serializedState !== "string" ||
new TextEncoder().encode(serializedState).length > 16 * 1024
) {
throw new TypeError(
"Widget state must be a JSON object of at most 16 KiB",
);
}
/** @type {unknown} */
const state = JSON.parse(serializedState);
if (
state == null ||
typeof state !== "object" ||
Array.isArray(state) ||
("imageIds" in state &&
(!Array.isArray(state.imageIds) || state.imageIds.length > 0))
) {
throw new TypeError("Widget state must be a JSON object");
}
const storedState = { modelContent: null, privateContent: null, ...state };
const canonicalState = JSON.stringify(
storedState,
(_key, /** @type {unknown} */ value) => {
if (typeof value === "number" && !Number.isFinite(value)) {
throw new TypeError("Widget state must contain finite numbers");
}
return value;
},
);
if (new TextEncoder().encode(canonicalState).length > 16 * 1024) {
throw new TypeError(
"Widget state must be a JSON object of at most 16 KiB",
);
}
return storedState;
};
let storage = null;
let state = null;
try {
const localStorage = globalThis.localStorage;
const probeKey = `${storageKey}:probe`;
localStorage.setItem(probeKey, "1");
localStorage.removeItem(probeKey);
storage = localStorage;
const savedState = storage.getItem(storageKey);
if (savedState != null) {
try {
state = parseState(savedState);
} catch {
// Invalid saved state does not disable future writes.
}
}
} catch {
storage = null;
}
const initialGlobals = {
widgetState: state,
statePersistence: storage == null ? "none" : "local",
};
const channel = new MessageChannel();
channel.port1.addEventListener("message", (event) => {
const data = event.data;
if (typeof data !== "object" || data == null) {
return;
}
if (data.type === "widget-state-write") {
if (!Number.isSafeInteger(data.id) || data.id <= 0) {
return;
}
let ok = false;
try {
if (storage != null) {
const state = parseState(data.state);
storage.setItem(storageKey, JSON.stringify(state));
ok = true;
}
} catch {
// Storage denial, quota limits, and invalid state return a failed receipt.
}
channel.port1.postMessage({
type: "widget-state-result",
id: data.id,
ok,
});
return;
}
if (data.type === "height") {
const height = data.height;
if (
typeof height === "number" &&
Number.isFinite(height) &&
height >= 0 &&
height <= 10_000
) {
frame.style.height = Math.ceil(height) + "px";
}
return;
}
if (data.type === "scroll-to") {
const top = data.top;
if (
typeof top === "number" &&
Number.isFinite(top) &&
top >= 0 &&
globalThis.navigator.userActivation?.isActive === true
) {
globalThis.scrollTo({
top: Math.ceil(
globalThis.scrollY + frame.getBoundingClientRect().top + top,
),
});
}
return;
}
if (data.type === "open-external") {
const rawHref = data.href;
if (typeof rawHref !== "string") {
return;
}
let href;
try {
const url = new URL(rawHref, document.baseURI);
if (url.protocol !== "https:") {
return;
}
href = url.href;
} catch {
return;
}
if (globalThis.navigator.userActivation?.isActive === true) {
const anchor = document.createElement("a");
anchor.href = href;
anchor.rel = "noopener noreferrer";
anchor.target = "_blank";
document.body.appendChild(anchor);
anchor.click();
anchor.remove();
}
return;
}
if (data.type === "follow-up") {
const prompt = data.prompt;
if (
typeof prompt !== "string" ||
globalThis.navigator.userActivation?.isActive !== true
) {
return;
}
const title = data.title;
globalThis.prompt(typeof title === "string" ? title : prompt, prompt);
}
});
channel.port1.start();
const srcdoc = frame.dataset.srcdoc;
if (srcdoc == null) {
return;
}
globalThis.addEventListener("resize", () => {
const height = Number.parseFloat(frame.style.height);
if (!Number.isFinite(height) || height <= viewportHeight) {
frame.style.removeProperty("height");
}
viewportHeight = globalThis.innerHeight;
channel.port1.postMessage({ type: "measure" });
});
frame.addEventListener(
"load",
() => {
frame.contentWindow?.postMessage(
{ type: "codex-visualization-initialize" },
"*",
[channel.port2],
);
},
{ once: true },
);
// Match the trusted bootstrap element, not the same text in an authored title.
frame.srcdoc = srcdoc.replace(
'<script type="application/json" id="codex-visualization-widget-state">__CODEX_VISUALIZATION_WIDGET_STATE__<\/script>',
() =>
'<script type="application/json" id="codex-visualization-widget-state">' +
JSON.stringify(initialGlobals).replaceAll("<", "\\u003c") +
"<\/script>",
);
delete frame.dataset.srcdoc;
})();
</script>
</body>
</html>