Spaces:
Running on Zero
Running on Zero
| /* The Kintsugi Garden — visual identity | |
| * | |
| * Palette tokens. The kintsugi seam (--kg-gold) is the only accent; | |
| * the rest of the surface is warm paper and charcoal-brown ink. | |
| */ | |
| :root { | |
| --kg-gold: #BF953F; | |
| --kg-gold-deep: #8C6A1F; | |
| --kg-paper: #F4EFE4; | |
| --kg-paper-deep: #EAE2D0; | |
| --kg-ink: #2B2622; | |
| --kg-ink-soft: #6F6558; | |
| --kg-crack: #D9C99A; | |
| color-scheme: light; | |
| } | |
| /* Defeat any automatic dark-mode that the user-agent or Gradio's runtime | |
| * may try to apply. The kintsugi metaphor — black ink on warm paper — | |
| * doesn't translate to dark mode, and the theme tokens we set above | |
| * already cover the main surfaces. These rules catch anything that | |
| * slips through (form controls, scrollbars, system UI). */ | |
| html, | |
| body, | |
| .dark, | |
| body.dark, | |
| .gradio-container, | |
| .gradio-container.dark { | |
| background-color: var(--kg-paper) ; | |
| color: var(--kg-ink) ; | |
| } | |
| .dark input, | |
| .dark textarea, | |
| .dark select, | |
| body.dark input, | |
| body.dark textarea, | |
| body.dark select { | |
| background-color: var(--kg-paper-deep) ; | |
| color: var(--kg-ink) ; | |
| border-color: var(--kg-crack) ; | |
| } | |
| .dark .block, | |
| .dark .form, | |
| .dark .panel, | |
| .dark .tabitem, | |
| .dark .markdown { | |
| background-color: var(--kg-paper) ; | |
| color: var(--kg-ink) ; | |
| } | |
| /* Page surface — narrower than Gradio's default, generous outer padding. */ | |
| .gradio-container { | |
| max-width: 1100px ; | |
| padding: 40px 24px 80px ; | |
| } | |
| /* Header band: mark on the left, wordmark + subtitle on the right, | |
| a single gold seam beneath the whole row. */ | |
| #kg-header h1 { | |
| font-size: 2.4rem; | |
| letter-spacing: -0.01em; | |
| margin: 0 0 4px; | |
| font-weight: 500; | |
| } | |
| #kg-header em { | |
| color: var(--kg-ink-soft); | |
| font-style: italic; | |
| } | |
| #kg-header { | |
| padding-bottom: 24px; | |
| border-bottom: 1px solid var(--kg-crack); | |
| position: relative; | |
| margin-bottom: 8px; | |
| } | |
| .kg-header-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 20px; | |
| } | |
| .kg-header-mark { | |
| flex: 0 0 76px; | |
| width: 76px; | |
| height: 76px; | |
| line-height: 0; | |
| } | |
| .kg-header-mark svg { | |
| display: block; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .kg-header-text { min-width: 0; } | |
| .kg-header-text p { margin: 0; } | |
| .kg-header-text .kg-tagline { | |
| margin-top: 4px; | |
| font-size: 0.86rem; | |
| opacity: 0.85; | |
| } | |
| @media (max-width: 560px) { | |
| .kg-header-row { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 12px; | |
| } | |
| } | |
| #kg-header::after { | |
| content: ""; | |
| position: absolute; | |
| left: 0; | |
| bottom: -1px; | |
| height: 2px; | |
| width: 120px; | |
| background: var(--kg-gold); | |
| } | |
| /* Disclaimer — a quiet framing line, not a slab. Gradio wraps the | |
| * Markdown in a .block container that defeats display:inline-block on | |
| * the host element, so we treat the disclaimer as a wide-but-light | |
| * band: smaller mono type, paper-deep wash, gold left-rule. The bold | |
| * "Disclaimer:" label still anchors the eye, but the line recedes | |
| * compared to the header above it. */ | |
| #kg-disclaimer { | |
| font-family: "SF Mono", "JetBrains Mono", ui-monospace, monospace; | |
| font-size: 0.74rem; | |
| line-height: 1.55; | |
| letter-spacing: 0.03em; | |
| color: var(--kg-ink-soft); | |
| border-left: 2px solid var(--kg-gold); | |
| padding: 8px 14px; | |
| background: var(--kg-paper-deep); | |
| margin: 12px 0 28px; | |
| } | |
| #kg-disclaimer strong { | |
| color: var(--kg-ink); | |
| font-weight: 500; | |
| } | |
| /* Privacy disclosure: quieter sibling to the disclaimer chip above — | |
| * same mono family + ink-soft color, but no gold rule, no wash, smaller | |
| * + lower opacity so it recedes. Honors the "data stays in your browser" | |
| * promise visibly without competing with the main disclaimer. */ | |
| #kg-privacy-note { | |
| font-family: "SF Mono", "JetBrains Mono", ui-monospace, monospace; | |
| font-size: 0.68rem; | |
| line-height: 1.5; | |
| letter-spacing: 0.03em; | |
| color: var(--kg-ink-soft); | |
| opacity: 0.75; | |
| padding: 0 16px; | |
| margin: -20px 0 24px; | |
| } | |
| /* Tabs: erase the default box; show a single gold underline on the | |
| * active tab that animates in like a freshly drawn seam. | |
| * | |
| * Selector note: Gradio 6.5.1 renders the tab strip as | |
| * .tabs > .tab-wrapper > .tab-container[role="tablist"] > button | |
| * earlier Gradio versions used .tab-nav. We list both so this CSS | |
| * survives a minor-version revert in either direction. The "kg-seam" | |
| * animation is load-bearing — it's the visual signature that gives the | |
| * page its kintsugi recurrence; without these selectors, Gradio's | |
| * theme accent leaks through and the seam never animates. */ | |
| .tab-nav, | |
| .tabs > .tab-wrapper, | |
| .tabs .tab-container[role="tablist"] { | |
| border-bottom: 1px solid var(--kg-crack) ; | |
| background: transparent ; | |
| } | |
| .tab-nav button, | |
| .tabs .tab-container[role="tablist"] > button { | |
| background: transparent ; | |
| border: none ; | |
| color: var(--kg-ink-soft) ; | |
| font-size: 0.95rem; | |
| padding: 12px 18px ; | |
| font-family: inherit; | |
| letter-spacing: 0.01em; | |
| } | |
| .tab-nav button.selected, | |
| .tabs .tab-container[role="tablist"] > button.selected, | |
| .tabs .tab-container[role="tablist"] > button[aria-selected="true"] { | |
| color: var(--kg-ink) ; | |
| position: relative; | |
| } | |
| .tab-nav button.selected::after, | |
| .tabs .tab-container[role="tablist"] > button.selected::after, | |
| .tabs .tab-container[role="tablist"] > button[aria-selected="true"]::after { | |
| content: ""; | |
| position: absolute; | |
| left: 18px; | |
| right: 18px; | |
| bottom: -1px; | |
| height: 2px; | |
| background: var(--kg-gold); | |
| animation: kg-seam 0.6s ease-out; | |
| } | |
| @keyframes kg-seam { | |
| from { transform: scaleX(0); transform-origin: left; } | |
| to { transform: scaleX(1); } | |
| } | |
| /* Tab overflow indicator — at narrow viewports Gradio 6.5.1 measures | |
| * the tab strip and hides tabs that don't fit behind a three-dot | |
| * overflow affordance. Untouched it renders in ink-black at low | |
| * contrast against paper, easy to miss. We tint it kg-gold so the | |
| * "more tabs hide here" signal reads as a kintsugi seam too. */ | |
| .tabs .overflow-menu > button { | |
| color: var(--kg-gold) ; | |
| padding: 0 8px ; | |
| } | |
| .tabs .overflow-menu > button:hover { | |
| color: var(--kg-gold-deep) ; | |
| } | |
| .tabs .overflow-dropdown { | |
| background: var(--kg-paper) ; | |
| border: 1px solid var(--kg-crack) ; | |
| border-top: 2px solid var(--kg-gold) ; | |
| border-radius: 0 ; | |
| } | |
| .tabs .overflow-dropdown button { | |
| color: var(--kg-ink) ; | |
| font-family: inherit ; | |
| padding: 10px 14px ; | |
| background: transparent ; | |
| } | |
| .tabs .overflow-dropdown button:hover { | |
| background: var(--kg-paper-deep) ; | |
| } | |
| /* Primary button: kintsugi gold, flat, with a subtle deeper-gold drop. */ | |
| button.primary, | |
| .primary { | |
| background: var(--kg-gold) ; | |
| color: var(--kg-paper) ; | |
| padding: 10px 28px ; | |
| font-family: inherit; | |
| letter-spacing: 0.02em; | |
| box-shadow: 0 1px 0 var(--kg-gold-deep) ; | |
| } | |
| button.primary:hover, | |
| .primary:hover { | |
| background: var(--kg-gold-deep) ; | |
| } | |
| /* Entry-type row: bottom-align the dropdown field with the Reflect | |
| * button. Without this, the button column stretches to fill the row | |
| * height while the dropdown's label sits at the top of its column — | |
| * the field and the button end up at different baselines and the | |
| * button looks oversized. We anchor everything to the bottom of the | |
| * row, constrain the Reflect button to roughly the dropdown field | |
| * height, and remove the wrapper column's default top padding so the | |
| * baselines line up cleanly. */ | |
| #kg-entry-row { | |
| align-items: flex-end ; | |
| } | |
| #kg-reflect-col { | |
| align-self: flex-end ; | |
| padding-top: 0 ; | |
| } | |
| #kg-reflect-btn { | |
| align-self: flex-end ; | |
| } | |
| #kg-reflect-btn button { | |
| min-height: 0 ; | |
| height: 48px ; | |
| padding: 0 28px ; | |
| line-height: 48px ; | |
| font-size: 1.05rem; | |
| } | |
| /* Secondary buttons — transparent with a gold border. */ | |
| button.secondary, | |
| .secondary { | |
| background: transparent ; | |
| border: 1px solid var(--kg-gold) ; | |
| color: var(--kg-ink) ; | |
| } | |
| /* Soul Map tables — strip the spreadsheet look; gold underline beneath | |
| * headers. The .dataframe selectors used to be enough when Gradio | |
| * rendered a pandas-styled table, but Gradio 6.x emits a Svelte | |
| * component tree whose classes are hashed (.svelte-XYZ) and the | |
| * .dataframe class no longer reaches the visible cells. We anchor on | |
| * the elem_id/elem_classes set in app.py and force the palette down | |
| * through every nested table-ish element. Heavy use of !important is | |
| * deliberate — Gradio's runtime applies inline styles that otherwise | |
| * win specificity. | |
| * | |
| * Selector coverage: real <table>/<thead>/<tbody>/<th>/<td>, ARIA-role | |
| * grid/row/cell variants (the editable variant uses divs with roles), | |
| * and the .cell-wrap wrapper that Gradio nests value cells inside. */ | |
| .kg-soul-table, | |
| .kg-soul-table * { | |
| font-family: inherit; | |
| } | |
| .kg-soul-table table, | |
| .kg-soul-table [role="grid"], | |
| .kg-soul-table [role="table"] { | |
| border-collapse: collapse; | |
| background-color: var(--kg-paper) ; | |
| } | |
| .kg-soul-table thead, | |
| .kg-soul-table thead tr, | |
| .kg-soul-table thead th, | |
| .kg-soul-table [role="rowgroup"]:first-child, | |
| .kg-soul-table [role="columnheader"] { | |
| background-color: var(--kg-paper-deep) ; | |
| color: var(--kg-ink-soft) ; | |
| font-weight: 500 ; | |
| text-transform: lowercase; | |
| letter-spacing: 0.04em; | |
| border-bottom: 1px solid var(--kg-gold) ; | |
| } | |
| .kg-soul-table tbody, | |
| .kg-soul-table tbody tr, | |
| .kg-soul-table tbody td, | |
| .kg-soul-table [role="rowgroup"]:not(:first-child) [role="row"], | |
| .kg-soul-table [role="cell"], | |
| .kg-soul-table [role="gridcell"] { | |
| background-color: var(--kg-paper) ; | |
| color: var(--kg-ink) ; | |
| border-color: var(--kg-crack) ; | |
| } | |
| .kg-soul-table tbody tr:nth-child(even) td, | |
| .kg-soul-table tbody tr:nth-child(even) [role="cell"], | |
| .kg-soul-table tbody tr:nth-child(even) [role="gridcell"] { | |
| background-color: var(--kg-paper-deep) ; | |
| } | |
| .kg-soul-table th, | |
| .kg-soul-table td { | |
| border: none ; | |
| padding: 10px 12px ; | |
| } | |
| /* The inner cell-wrap div Gradio uses for value content. */ | |
| .kg-soul-table .cell-wrap, | |
| .kg-soul-table .table-wrap { | |
| background-color: transparent ; | |
| color: var(--kg-ink) ; | |
| } | |
| /* Dark-mode runtime override — Gradio sometimes flips internal cell | |
| * backgrounds to a CSS variable like --table-row-bg that resolves to | |
| * a dark color under .dark. Pin those variables here. */ | |
| .dark .kg-soul-table, | |
| .kg-soul-table.dark { | |
| --table-row-bg: var(--kg-paper); | |
| --table-row-bg-odd: var(--kg-paper); | |
| --table-row-bg-even: var(--kg-paper-deep); | |
| --table-header-bg: var(--kg-paper-deep); | |
| --table-header-fg: var(--kg-ink-soft); | |
| --table-text-color: var(--kg-ink); | |
| --table-border-color: var(--kg-crack); | |
| --body-text-color: var(--kg-ink); | |
| } | |
| /* The Mandala — fixed-aspect square with a hairline gold frame. */ | |
| #kg-mandala { | |
| aspect-ratio: 1 / 1; | |
| border: 1px solid var(--kg-crack); | |
| padding: 12px; | |
| background: var(--kg-paper); | |
| box-shadow: | |
| 0 0 0 8px var(--kg-paper), | |
| 0 0 0 9px var(--kg-gold); | |
| position: relative; | |
| } | |
| /* Empty-mandala state: before the first Reflect, Gradio's image | |
| * component renders a large grey placeholder icon. On a quiet paper | |
| * surface that placeholder reads as broken-image clutter, not as | |
| * "awaiting input." We use :has() to detect the absence of a populated | |
| * <img> and (a) hide the Gradio default chrome inside the frame, and | |
| * (b) draw an ink-soft italic line centred in the empty square. Once | |
| * a real <img src="..."> appears, the rules deactivate and the | |
| * mandala renders normally. */ | |
| #kg-mandala:not(:has(img[src])) > * { | |
| visibility: hidden; | |
| } | |
| #kg-mandala:not(:has(img[src]))::after { | |
| content: "A mandala will appear here."; | |
| position: absolute; | |
| inset: 12px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| text-align: center; | |
| font-style: italic; | |
| color: var(--kg-ink-soft); | |
| font-size: 0.95rem; | |
| visibility: visible; | |
| padding: 0 16px; | |
| } | |
| /* Markdown reading prose — serif, generous leading, gold-deep headings. */ | |
| .prose, | |
| .markdown { | |
| font-size: 1.05rem; | |
| line-height: 1.75; | |
| color: var(--kg-ink); | |
| } | |
| .prose h2, | |
| .markdown h2 { | |
| font-size: 1.15rem; | |
| font-weight: 500; | |
| color: var(--kg-gold-deep); | |
| letter-spacing: 0.02em; | |
| margin: 28px 0 10px; | |
| } | |
| /* h3 voice — used by Soul Map's "Symbols" / "Themes" headings. Echoes | |
| * the gold-deep h2 voice one notch smaller so the section labels read | |
| * as kin to the reading prose above. */ | |
| .prose h3, | |
| .markdown h3 { | |
| font-size: 0.95rem; | |
| font-weight: 500; | |
| color: var(--kg-gold-deep); | |
| letter-spacing: 0.06em; | |
| text-transform: lowercase; | |
| margin: 24px 0 8px; | |
| } | |
| /* Footer note — italic serif, centered, ink-soft. The horizontal rule | |
| * carries a small gold pinpoint at its centre — a final echo of the | |
| * kintsugi seam from the header, closing the page with the same | |
| * signature it opened with. */ | |
| #kg-footer { | |
| text-align: center; | |
| color: var(--kg-ink-soft); | |
| font-style: italic; | |
| margin-top: 40px; | |
| } | |
| #kg-footer hr { | |
| border: none; | |
| border-top: 1px solid var(--kg-crack); | |
| margin: 32px auto 16px; | |
| max-width: 200px; | |
| position: relative; | |
| } | |
| #kg-footer hr::after { | |
| content: ""; | |
| position: absolute; | |
| left: 50%; | |
| top: -3px; | |
| width: 7px; | |
| height: 7px; | |
| background: var(--kg-gold); | |
| border-radius: 50%; | |
| transform: translateX(-50%); | |
| } | |
| /* Gradio's default page footer ("Use via API · Built with Gradio · | |
| * Settings"). On a paper-and-ink surface the row reads as foreign | |
| * chrome — we tone it down to small mono in ink-soft so it recedes | |
| * beneath the kg-footer prose. Attribution stays visible; volume | |
| * drops. */ | |
| footer.svelte-zxu34v, | |
| .gradio-container > footer, | |
| body > footer { | |
| font-family: "SF Mono", "JetBrains Mono", ui-monospace, monospace ; | |
| font-size: 0.7rem ; | |
| color: var(--kg-ink-soft) ; | |
| opacity: 0.75; | |
| margin-top: 8px ; | |
| padding-top: 12px ; | |
| } | |
| footer.svelte-zxu34v button, | |
| footer.svelte-zxu34v a, | |
| .gradio-container > footer button, | |
| .gradio-container > footer a { | |
| color: var(--kg-ink-soft) ; | |
| font-family: inherit ; | |
| font-size: inherit ; | |
| } | |
| footer.svelte-zxu34v img, | |
| .gradio-container > footer img { | |
| opacity: 0.55; | |
| } | |
| /* Mobile adjustments. */ | |
| @media (max-width: 640px) { | |
| .gradio-container { | |
| padding: 24px 14px 48px ; | |
| } | |
| #kg-header h1 { | |
| font-size: 1.7rem; | |
| } | |
| #kg-mandala { | |
| box-shadow: | |
| 0 0 0 4px var(--kg-paper), | |
| 0 0 0 5px var(--kg-gold); | |
| } | |
| } | |