/* Every colour on the page. The ground and ink are did.bot's green-black and green-white (site/src/styles/global.css); the neon is this page's own. Every text colour here is at least 4.5:1 against --ground, --panel, --inset and --lattice-on, and --line at least 3:1. */ :root { color-scheme: dark; --ground: #040d08; --panel: #0a1a11; --inset: #020705; --ink: #e2f5e8; --muted: #8fbb9c; --neon: #39ff88; --line: #1c7f45; --glow: rgba(57, 255, 136, 0.28); --warn: #ffc24d; --error: #ff6b86; /* The lattice behind the page, drawn by src/lattice.ts. */ --lattice-on: #0b2616; --lattice-off: #061209; /* Highlighted JSON and Cedar: src/ui/policy-syntax.ts, src/ui/regex-parts.ts and src/ui/cedar-tokens.ts. */ --syn-punct: #72a283; --syn-key: #7ee0b5; --syn-string: var(--ink); --syn-number: #ffb870; --syn-literal: #ff8fcf; --syn-type: var(--neon); --syn-kind: #d4ff4f; --syn-ref: #6fd8ff; --syn-regex: #ffe07a; --syn-regex-op: #6fd8ff; --syn-regex-escape: #ff9a57; --syn-regex-class: #a9b4ff; --syn-regex-group: #ff8fcf; --syn-placeholder: #e0b3ff; /* Cedar's own roles, in colours the JSON already uses. */ --syn-var: var(--syn-number); --syn-op: var(--syn-regex-op); --syn-annotation: var(--syn-literal); --syn-comment: var(--muted); --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace; font: 15px/1.55 var(--mono); color: var(--ink); background: var(--ground); accent-color: var(--neon); scrollbar-color: var(--line) var(--inset); } body { margin: 0; } /* The lattice, and over it scanlines and a vignette. Both sit behind the page and take no input. */ #lattice, body::after { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; } #lattice { image-rendering: pixelated; } body::after { content: ""; background: repeating-linear-gradient(to bottom, transparent 0 2px, rgba(0, 0, 0, 0.28) 2px 3px), radial-gradient(ellipse at 50% 45%, transparent 45%, rgba(0, 0, 0, 0.6) 100%); } ::selection { background: var(--neon); color: var(--ground); } .masthead, main { max-width: 64rem; margin: 0 auto; padding: 1rem 1.25rem; } .masthead { border-bottom: 1px solid var(--line); } main { padding-bottom: 4rem; } h1, h2, h3 { font-weight: 600; line-height: 1.3; } h1 { font-size: 1.5rem; margin: 0.5rem 0; color: var(--neon); letter-spacing: 0.04em; text-shadow: 0 0 12px var(--glow); } h2 { font-size: 1.15rem; margin: 2.25rem 0 0.5rem; color: var(--neon); text-shadow: 0 0 10px var(--glow); } h2::before { content: "// "; content: "// " / ""; color: var(--muted); text-shadow: none; } h3 { font-size: 1rem; margin: 0 0 0.5rem; color: var(--ink); overflow-wrap: anywhere; } /* The UA sheet sets these to the bare generic `monospace`. */ pre, code { font-family: var(--mono); } pre, textarea.mono { font-size: 0.9rem; } .mono, code { overflow-wrap: anywhere; } .hint { color: var(--muted); font-size: 0.9rem; } .account { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; } dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.2rem 0.75rem; margin: 0.5rem 0; } dt { color: var(--muted); } dd { margin: 0; } .cards { list-style: none; padding: 0; display: grid; gap: 0.75rem; } .card, .editor { background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 0.75rem 1rem; } .editor { display: grid; gap: 0.75rem; margin: 0.75rem 0; border-color: var(--neon); box-shadow: 0 0 0 1px var(--glow), 0 0 18px var(--glow); } .editor h3 { color: var(--neon); } .field { display: grid; gap: 0.25rem; } label, legend, .label { font-weight: 600; } /* src/ui/document-editor.ts: CodeMirror, in a shadow root of its own. */ .document-editor { display: block; border-radius: 4px; } .document-editor:focus-within { outline: 2px solid var(--neon); outline-offset: 2px; box-shadow: 0 0 12px var(--glow); } .field-problems:empty { display: none; } input:not([type="checkbox"]):not([type="radio"]), select, textarea { font: inherit; color: inherit; background: var(--inset); border: 1px solid var(--line); border-radius: 4px; padding: 0.35rem 0.5rem; width: 100%; box-sizing: border-box; } input:not([type="checkbox"]):not([type="radio"]):focus, select:focus, textarea:focus { border-color: var(--neon); } input[readonly] { color: var(--muted); border-style: dashed; } textarea.mono { line-height: 1.5; caret-color: var(--neon); } fieldset.checkboxes { border: 1px solid var(--line); border-radius: 4px; display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; } fieldset.checkboxes label { font-weight: normal; } form.sign-in { display: grid; gap: 0.5rem; max-width: 28rem; } a { color: var(--neon); } button, a.button { display: inline-block; text-decoration: none; line-height: normal; } button, a.button { font: inherit; padding: 0.3rem 0.8rem; border: 1px solid var(--line); border-radius: 4px; background: var(--inset); color: var(--ink); cursor: pointer; } button:hover:not(:disabled), a.button:hover { border-color: var(--neon); color: var(--neon); box-shadow: 0 0 8px var(--glow); } button[type="submit"], .primary, a.button.primary { border-color: var(--neon); color: var(--neon); } button[type="submit"]:hover:not(:disabled), .primary:hover:not(:disabled), a.button.primary:hover { background: var(--neon); color: var(--ground); } button.danger, button.danger:hover:not(:disabled) { border-color: var(--error); color: var(--error); box-shadow: none; } button.danger:hover:not(:disabled) { background: var(--error); color: var(--ground); } button.link, button.link:hover:not(:disabled) { border: none; background: none; box-shadow: none; padding: 0 0.25rem; } button:disabled { opacity: 0.5; border-style: dashed; cursor: default; } @media (prefers-reduced-motion: no-preference) { button { transition: background-color 120ms, box-shadow 120ms; } } .buttons { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; } .status { margin: 0.25rem 0; font-weight: 600; } .status.ok, .status.matching { color: var(--neon); } .status.behind, .status.checking { color: var(--warn); } .status.error, .status.unreachable { color: var(--error); } .problems { list-style: none; margin: 0.25rem 0; padding: 0; display: grid; gap: 0.35rem; } .problems li { border-left: 3px solid; padding: 0.15rem 0 0.15rem 0.6rem; } .problems .error { border-color: var(--error); } .problems .warning { border-color: var(--warn); } /* A message may be several lines, such as a regex error with its caret. */ .problems .message { display: block; white-space: pre-wrap; font-size: 0.9rem; } .problems .severity { font-weight: 700; text-transform: uppercase; font-size: 0.75rem; letter-spacing: 0.05em; border: 1px solid; border-radius: 3px; padding: 0 0.3rem; } .problems .error .severity { color: var(--error); } .problems .warning .severity { color: var(--warn); } pre.verbatim { background: var(--inset); border: 1px solid var(--line); border-radius: 4px; padding: 0.5rem 0.75rem; overflow-x: auto; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 24rem; line-height: 1.5; } /* Highlighted JSON and Cedar. The class names are src/ui/policy-syntax.ts's, src/ui/regex-parts.ts's and src/ui/cedar-tokens.ts's. */ .tok-punct { color: var(--syn-punct); } .tok-key { color: var(--syn-key); } .tok-str { color: var(--syn-string); } .tok-num { color: var(--syn-number); } .tok-lit { color: var(--syn-literal); } .tok-type { color: var(--syn-type); text-shadow: 0 0 8px var(--glow); } .tok-kind { color: var(--syn-kind); font-weight: 700; } .tok-did, .tok-uri { color: var(--syn-ref); } .tok-var { color: var(--syn-var); } .tok-op { color: var(--syn-op); } .tok-anno { color: var(--syn-annotation); } .tok-comment { color: var(--syn-comment); font-style: italic; } .tok-regex { color: var(--syn-regex); } .re-op { color: var(--syn-regex-op); font-weight: 700; } .re-escape { color: var(--syn-regex-escape); } .re-class { color: var(--syn-regex-class); } .re-group { color: var(--syn-regex-group); font-weight: 700; } .tok-ph { color: var(--syn-placeholder); background: rgba(224, 179, 255, 0.1); outline: 1px dashed var(--syn-placeholder); outline-offset: -1px; border-radius: 2px; } .picker { display: grid; gap: 0.25rem; position: relative; } .chips { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.25rem; } .chip { border: 1px solid var(--line); border-radius: 999px; padding: 0.1rem 0.25rem 0.1rem 0.6rem; background: var(--inset); } .detail { color: var(--muted); font-size: 0.85rem; } .suggestions { list-style: none; margin: 0; padding: 0; border: 1px solid var(--neon); border-radius: 4px; background: var(--panel); box-shadow: 0 0 12px var(--glow); max-height: 16rem; overflow-y: auto; } .suggestions [role="option"] { padding: 0.3rem 0.5rem; cursor: pointer; overflow-wrap: anywhere; } .suggestions [aria-selected="true"], .suggestions [data-combobox-option-default="true"] { background: var(--neon); color: var(--ground); } .suggestions [aria-selected="true"] .detail, .suggestions [data-combobox-option-default="true"] .detail { color: inherit; } :focus-visible { outline: 2px solid var(--neon); outline-offset: 2px; box-shadow: 0 0 12px var(--glow); } [hidden] { display: none !important; } /* Sections: a nav of links, one per view. */ .tabs ul { list-style: none; display: flex; flex-wrap: wrap; gap: 0.25rem; margin: 1rem 0 0; padding: 0; } .tabs a { display: inline-block; padding: 0.35rem 0.9rem; border: 1px solid var(--line); border-bottom: none; border-radius: 4px 4px 0 0; background: var(--inset); color: var(--ink); text-decoration: none; } .tabs a[aria-current="page"] { border-color: var(--neon); color: var(--neon); box-shadow: 0 0 10px var(--glow); } .tabs a[aria-disabled="true"] { color: var(--muted); border-style: dashed; cursor: not-allowed; } /* The batch, above every view while signed in. */ .tray { max-width: 64rem; margin: 0.75rem auto 0; padding: 0 1.25rem; } .tray-body { background: var(--panel); border: 1px solid var(--warn); border-radius: 6px; padding: 0.5rem 1rem; } .tray-body summary { cursor: pointer; font-weight: 600; color: var(--warn); } ul.plain { list-style: none; margin: 0.25rem 0; padding: 0; display: grid; gap: 0.25rem; } .op-kind { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid; border-radius: 3px; padding: 0 0.3rem; } .op.create .op-kind { color: var(--neon); } .op.update .op-kind { color: var(--warn); } .op.delete .op-kind { color: var(--error); } /* Whether a policy applies to anyone. Unbound is loud on purpose. */ .status.applied { color: var(--neon); } .status.unbound { color: var(--warn); border: 1px solid var(--warn); border-left-width: 4px; border-radius: 4px; padding: 0.25rem 0.6rem; background: var(--inset); } .callout.unbound { margin: 0.5rem 0; } /* The steps of a flow. */ .steps { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0 1rem; padding: 0; } .steps li { padding: 0.2rem 0.7rem; border: 1px dashed var(--line); border-radius: 999px; color: var(--muted); background: var(--inset); } .steps li.reached { border-style: solid; color: var(--ink); } .steps li[aria-current="step"] { border: 1px solid var(--neon); color: var(--neon); box-shadow: 0 0 10px var(--glow); font-weight: 600; } /* Back and Next stay in view however long the step. */ .wizard-nav { position: sticky; bottom: 0; z-index: 1; margin-top: 1rem; padding: 0.6rem 0; border-top: 1px solid var(--line); background: var(--ground); } fieldset.choices { border: 1px solid var(--line); border-radius: 4px; display: grid; gap: 0.35rem; margin: 0.75rem 0; } fieldset.choices label { font-weight: normal; } .diff { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 0.75rem; } .diff-side { min-width: 0; } /* The catalog: names and descriptions only. */ .catalog { list-style: none; padding: 0; display: grid; gap: 0.5rem; } .catalog li { background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 0.5rem 1rem; } .catalog p { margin: 0.25rem 0 0; } .choice button { padding: 0.5rem 1rem; } .visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } /* A heading focused so a screen reader starts there, not as a control. */ h2[tabindex="-1"]:focus { outline: none; box-shadow: none; } .wizard h3 { margin: 1.25rem 0 0.4rem; } .card h4 { margin: 0 0 0.5rem; } /* How many policies apply to nobody, on the tab that lists them. */ .badge { margin-left: 0.4rem; padding: 0 0.35rem; border: 1px solid var(--warn); border-radius: 999px; color: var(--warn); font-size: 0.8rem; font-weight: 700; } /* The confirm step's own warning: louder than a line of text. */ .callout.unbound { border: 1px solid var(--warn); border-left-width: 4px; border-radius: 4px; background: var(--inset); padding: 0.5rem 1rem; } .callout.unbound h4 { margin: 0.25rem 0; color: var(--warn); } /* Saving a policy that applies to nobody: a way off the way through. */ .unbound-path { margin-top: 1rem; border: 1px dashed var(--line); border-radius: 4px; padding: 0.4rem 0.8rem; } .unbound-path summary { cursor: pointer; color: var(--muted); } .status.saved { border: 1px solid var(--neon); border-radius: 4px; padding: 0.35rem 0.8rem; background: var(--inset); } /* The simulator: what is being asked on one side, what the policies say on the other. */ .simulator .panes { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem; align-items: start; } @media (max-width: 900px) { .simulator .panes { grid-template-columns: minmax(0, 1fr); } } .simulator fieldset { margin: 0 0 1rem; } .simulator textarea.request { width: 100%; resize: vertical; } .simulator .toggle { display: block; margin: 0.35rem 0; } .simulator .advanced { border: 1px dashed var(--line); border-radius: 4px; padding: 0.4rem 0.8rem; } .simulator .advanced summary { cursor: pointer; color: var(--muted); } /* The verdict, loud either way: it is the answer the page exists to give. */ .verdict .status { border: 1px solid; border-left-width: 4px; border-radius: 4px; padding: 0.5rem 0.8rem; background: var(--inset); font-size: 1.05rem; } .verdict .status.allowed { color: var(--neon); } .verdict .status.denied { color: var(--error); } .policy-rows { list-style: none; margin: 0.5rem 0; padding: 0; display: grid; gap: 0.5rem; } .policy-rows li { border: 1px solid var(--line); border-left-width: 4px; border-radius: 4px; padding: 0.35rem 0.7rem; background: var(--panel); } .policy-rows li.deny, .policy-rows li.freeze { border-color: var(--error); } .policy-rows li.allow { border-color: var(--line); } .policy-rows.quiet li { border-left-width: 1px; background: none; color: var(--muted); } .policy-row .outcome { font-weight: 600; margin-right: 0.5rem; } .policy-row.deny .outcome, .policy-row.freeze .outcome { color: var(--error); } .policy-row.allow .outcome { color: var(--neon); } .policy-row .where, .policy-row .action { display: block; color: var(--muted); font-size: 0.9em; } .policy-row .reason { margin: 0.25rem 0 0; } .limits { margin-top: 1.5rem; color: var(--muted); } .limits ul { margin: 0.25rem 0; padding-left: 1.2rem; }