Kintsugi-Garden / kintsugi.css
AI-Sherpa
Initial commit: Kintsugi Garden — Build Small Hackathon submission
4f04d05
/* 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) !important;
color: var(--kg-ink) !important;
}
.dark input,
.dark textarea,
.dark select,
body.dark input,
body.dark textarea,
body.dark select {
background-color: var(--kg-paper-deep) !important;
color: var(--kg-ink) !important;
border-color: var(--kg-crack) !important;
}
.dark .block,
.dark .form,
.dark .panel,
.dark .tabitem,
.dark .markdown {
background-color: var(--kg-paper) !important;
color: var(--kg-ink) !important;
}
/* Page surface — narrower than Gradio's default, generous outer padding. */
.gradio-container {
max-width: 1100px !important;
padding: 40px 24px 80px !important;
}
/* 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) !important;
background: transparent !important;
}
.tab-nav button,
.tabs .tab-container[role="tablist"] > button {
background: transparent !important;
border: none !important;
color: var(--kg-ink-soft) !important;
font-size: 0.95rem;
padding: 12px 18px !important;
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) !important;
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) !important;
padding: 0 8px !important;
}
.tabs .overflow-menu > button:hover {
color: var(--kg-gold-deep) !important;
}
.tabs .overflow-dropdown {
background: var(--kg-paper) !important;
border: 1px solid var(--kg-crack) !important;
border-top: 2px solid var(--kg-gold) !important;
border-radius: 0 !important;
}
.tabs .overflow-dropdown button {
color: var(--kg-ink) !important;
font-family: inherit !important;
padding: 10px 14px !important;
background: transparent !important;
}
.tabs .overflow-dropdown button:hover {
background: var(--kg-paper-deep) !important;
}
/* Primary button: kintsugi gold, flat, with a subtle deeper-gold drop. */
button.primary,
.primary {
background: var(--kg-gold) !important;
color: var(--kg-paper) !important;
padding: 10px 28px !important;
font-family: inherit;
letter-spacing: 0.02em;
box-shadow: 0 1px 0 var(--kg-gold-deep) !important;
}
button.primary:hover,
.primary:hover {
background: var(--kg-gold-deep) !important;
}
/* 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 !important;
}
#kg-reflect-col {
align-self: flex-end !important;
padding-top: 0 !important;
}
#kg-reflect-btn {
align-self: flex-end !important;
}
#kg-reflect-btn button {
min-height: 0 !important;
height: 48px !important;
padding: 0 28px !important;
line-height: 48px !important;
font-size: 1.05rem;
}
/* Secondary buttons — transparent with a gold border. */
button.secondary,
.secondary {
background: transparent !important;
border: 1px solid var(--kg-gold) !important;
color: var(--kg-ink) !important;
}
/* 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) !important;
}
.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) !important;
color: var(--kg-ink-soft) !important;
font-weight: 500 !important;
text-transform: lowercase;
letter-spacing: 0.04em;
border-bottom: 1px solid var(--kg-gold) !important;
}
.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) !important;
color: var(--kg-ink) !important;
border-color: var(--kg-crack) !important;
}
.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) !important;
}
.kg-soul-table th,
.kg-soul-table td {
border: none !important;
padding: 10px 12px !important;
}
/* The inner cell-wrap div Gradio uses for value content. */
.kg-soul-table .cell-wrap,
.kg-soul-table .table-wrap {
background-color: transparent !important;
color: var(--kg-ink) !important;
}
/* 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 !important;
font-size: 0.7rem !important;
color: var(--kg-ink-soft) !important;
opacity: 0.75;
margin-top: 8px !important;
padding-top: 12px !important;
}
footer.svelte-zxu34v button,
footer.svelte-zxu34v a,
.gradio-container > footer button,
.gradio-container > footer a {
color: var(--kg-ink-soft) !important;
font-family: inherit !important;
font-size: inherit !important;
}
footer.svelte-zxu34v img,
.gradio-container > footer img {
opacity: 0.55;
}
/* Mobile adjustments. */
@media (max-width: 640px) {
.gradio-container {
padding: 24px 14px 48px !important;
}
#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);
}
}