diff --git a/app/battles/[slug]/page.module.scss b/app/battles/[slug]/page.module.scss index 4d3c7666..247f7e1b 100644 --- a/app/battles/[slug]/page.module.scss +++ b/app/battles/[slug]/page.module.scss @@ -62,7 +62,9 @@ display: block; width: 100%; height: auto; - border-radius: 0.25rem; + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; } .back { @@ -70,14 +72,15 @@ padding-bottom: 3rem; font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/app/battles/page.module.scss b/app/battles/page.module.scss index 3174c87f..e7284634 100644 --- a/app/battles/page.module.scss +++ b/app/battles/page.module.scss @@ -22,8 +22,9 @@ .warName { font-family: var(--font-character-name); font-size: 1.5rem; - color: var(--ink); - border-bottom: 1px solid var(--gold-leaf); + color: var(--gaslight); + letter-spacing: 0.04em; + border-bottom: 1px solid var(--edge-brass); padding-bottom: 0.35rem; } @@ -46,45 +47,56 @@ gap: 1rem; align-items: baseline; padding: 0.5rem 0.75rem; - border-radius: 0.25rem; + border-radius: var(--bevel-sm); + corner-shape: bevel; text-decoration: none; - color: var(--ink); + color: var(--gaslight); transition: background-color 0.15s ease; &:hover { - background-color: rgba(212, 162, 89, 0.16); + background-color: rgba(176, 129, 58, 0.08); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; } } .name { - display: inline-flex; + display: inline-grid; + grid-auto-flow: column; align-items: center; + justify-content: start; gap: 0.5rem; font-family: var(--font-ui); font-size: 1.05rem; } +// A charged node marks a battle with a full account behind it. .indicator { - flex: none; width: 0.5rem; height: 0.5rem; border-radius: 50%; - background-color: var(--gold-leaf); + background-color: var(--verdigris); + box-shadow: + inset 0 1px 1px rgba(255, 255, 255, 0.28), + 0 1px 2px rgba(8, 5, 2, 0.5); } .when { font-family: var(--font-ui); font-size: 0.9rem; - color: var(--ink-faded); + color: var(--gaslight-faint); white-space: nowrap; + font-variant-numeric: tabular-nums; } .legend { margin-top: 2rem; font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); - font-style: italic; + color: var(--gaslight-faint); + letter-spacing: 0.04em; @media (max-width: bp.$md) { padding-left: 1.5rem; diff --git a/app/characters/[slug]/page.module.scss b/app/characters/[slug]/page.module.scss index 832cf891..1a962f18 100644 --- a/app/characters/[slug]/page.module.scss +++ b/app/characters/[slug]/page.module.scss @@ -7,30 +7,38 @@ } .heading { - display: flex; + display: grid; + grid-auto-flow: column; + grid-auto-columns: max-content; align-items: center; justify-content: center; gap: 0.85rem; - flex-wrap: wrap; width: 100%; h1 { font-family: var(--font-character-name); - font-variant: normal; - font-weight: 600; - letter-spacing: 1px; + text-transform: none; + font-weight: 400; + letter-spacing: 0.06em; font-size: 2.6rem; line-height: 1.1; } + // Stack on small screens with the name above the sigil. @media (max-width: bp.$md) { - flex-direction: column-reverse; + grid-auto-flow: row; + grid-auto-columns: auto; + justify-items: center; gap: 0.5rem; + + > h1 { + order: -1; + } } } .sigilLink { - display: inline-flex; + display: inline-grid; border-radius: 2px; &:hover, @@ -41,12 +49,12 @@ .alias { font-family: var(--font-body); - font-variant: normal; + text-transform: none; font-style: italic; font-weight: 400; letter-spacing: 0; font-size: 0.6em; - color: var(--ink-faded); + color: var(--gaslight-dim); text-align: left; @media (max-width: bp.$md) { @@ -59,14 +67,15 @@ } .portrait { - display: flex; - justify-content: center; + display: grid; + justify-items: center; width: 100%; max-width: 1100px; - border-radius: 30px; - corner-shape: scoop; + border-radius: 24px; + corner-shape: bevel; overflow: hidden; - border: 1px solid var(--ink-faded); + border: 1px solid var(--edge-strong); + box-shadow: var(--shadow-raised); @media (max-width: bp.$md) { width: 100vw; @@ -79,7 +88,7 @@ } } -// Regional border tints for `.portrait` — colours live in `globals.scss` +// Regional border tints for `.portrait`; colours live in `globals.scss` // as `--region-color-*` tokens (shared with `FilteredHouseList`). .portraitNorth { border-color: var(--region-color-north); @@ -119,24 +128,25 @@ gap: 0.6rem 1.5rem; width: 100%; padding: 1rem 1.25rem; - background: rgba(248, 236, 208, 0.5); - border: 1px solid rgba(107, 68, 35, 0.2); - border-radius: 2px; + background: rgba(22, 17, 12, 0.55); + border: 1px solid var(--edge); + border-radius: var(--bevel-sm); + corner-shape: bevel; font-family: var(--font-ui); - font-size: 0.9rem; + font-size: 0.95rem; + letter-spacing: 0.03em; dt { - flex: 0 0 auto; - color: var(--ink-faded); + color: var(--brass); font-family: var(--font-sans); + font-weight: 500; text-transform: uppercase; - letter-spacing: 1px; + letter-spacing: 0.12em; font-size: 0.72rem; - font-weight: 500; } dd { - color: var(--ink); + color: var(--gaslight); margin: 0; padding: 0; } @@ -170,14 +180,14 @@ .unaffiliated { font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); } .titles { list-style: none; padding: 0; - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: 1fr; gap: 0.15rem; } @@ -187,31 +197,28 @@ .relations { padding: 1rem 1.25rem; - background: rgba(248, 236, 208, 0.4); - border: 1px solid rgba(107, 68, 35, 0.15); - border-radius: 2px; + background: rgba(22, 17, 12, 0.45); + border: 1px solid var(--edge); + border-radius: var(--bevel-sm); + corner-shape: bevel; font-family: var(--font-ui); - font-size: 0.92rem; - display: flex; - flex-direction: column; + font-size: 0.95rem; + letter-spacing: 0.03em; + display: grid; + grid-template-columns: 1fr; gap: 0.5rem; dt { - flex: 0 0 5.5rem; - color: var(--ink-faded); + color: var(--brass); font-family: var(--font-sans); + font-weight: 500; text-transform: uppercase; - letter-spacing: 1px; + letter-spacing: 0.12em; font-size: 0.72rem; - font-weight: 500; - - @media (max-width: bp.$sm) { - flex-basis: 100%; - } } dd { - color: var(--ink); + color: var(--gaslight); } @media (max-width: bp.$md) { @@ -221,14 +228,19 @@ } .relationsRow { - display: flex; + display: grid; + grid-template-columns: 5.5rem 1fr; gap: 0.6rem; align-items: baseline; - flex-wrap: wrap; + + @media (max-width: bp.$sm) { + grid-template-columns: 1fr; + gap: 0.15rem; + } } .placeholder { - color: var(--ink-faded); + color: var(--gaslight-dim); font-style: italic; } @@ -243,14 +255,15 @@ .back { font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/app/design/page.module.scss b/app/design/page.module.scss index 55d3d503..11e81abb 100644 --- a/app/design/page.module.scss +++ b/app/design/page.module.scss @@ -14,36 +14,37 @@ .sectionIntro { font-family: var(--font-body); - font-size: 1rem; + font-size: 1.05rem; font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); max-width: 62ch; } .divider { width: min(320px, 60%); - color: var(--ink-faded); - opacity: 0.7; + color: var(--brass); + opacity: 0.85; } .code { - font-family: var(--font-sans); - font-size: 0.85em; + font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace; + font-size: 0.8em; font-style: normal; - color: var(--ink); - background: rgba(139, 26, 26, 0.08); - border: 1px solid rgba(107, 68, 35, 0.2); - border-radius: 2px; + color: var(--verdigris); + background: var(--well); + border: 1px solid var(--edge); + border-radius: 3px; padding: 0.1em 0.4em; } -// The shared small-caps caption idiom used for every token/spec label. +// The shared engraved caption idiom used for every token/spec label. .specLabel { font-family: var(--font-ui); - font-variant: small-caps; - font-size: 0.85rem; - letter-spacing: 1.5px; - color: var(--ink-faded); + font-weight: 500; + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 0.16em; + color: var(--gaslight-faint); } // ------------------------------------------------------------------ colour @@ -64,11 +65,12 @@ .swatchChip { height: 76px; - border-radius: 2px; - border: 1px solid rgba(61, 40, 23, 0.25); + border-radius: var(--bevel-sm); + corner-shape: bevel; + border: 1px solid var(--edge-strong); box-shadow: - inset 0 0 0 1px rgba(248, 236, 208, 0.25), - 0 1px 3px rgba(61, 40, 23, 0.18); + inset 0 1px 0 rgba(255, 255, 255, 0.07), + var(--shadow-soft); } .swatchMeta { @@ -79,15 +81,16 @@ .swatchName { font-family: var(--font-ui); font-size: 1rem; - color: var(--ink); + letter-spacing: 0.03em; + color: var(--gaslight); } .swatchToken, .swatchHex { - font-family: var(--font-sans); - font-size: 0.75rem; - color: var(--ink-faded); - letter-spacing: 0.3px; + font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace; + font-size: 0.7rem; + color: var(--gaslight-faint); + letter-spacing: 0.02em; } .swatchHex { @@ -107,7 +110,7 @@ gap: 1.25rem; align-items: baseline; padding-bottom: 1.25rem; - border-bottom: 1px dashed rgba(107, 68, 35, 0.22); + border-bottom: 1px dashed var(--edge); @media (max-width: bp.$sm) { grid-template-columns: 1fr; @@ -138,7 +141,7 @@ .fontSample { font-size: 1.6rem; - color: var(--ink); + color: var(--gaslight); line-height: 1.3; } @@ -159,18 +162,19 @@ align-items: center; } +// Each step runs from brass to verdigris, polished metal into patina. .spaceBar { height: 18px; min-width: 4px; border-radius: 2px; - background: linear-gradient(90deg, var(--wax-seal), var(--rose-madder)); + background: linear-gradient(90deg, var(--brass), var(--verdigris)); justify-self: start; } .spacePx { - font-family: var(--font-sans); - font-size: 0.75rem; - color: var(--ink-faded); + font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace; + font-size: 0.7rem; + color: var(--gaslight-faint); } .spaceDemos { @@ -189,9 +193,9 @@ .padBox { display: grid; padding: 1.5rem; - border: 1px dashed rgba(107, 68, 35, 0.4); - border-radius: 2px; - background: rgba(212, 162, 89, 0.12); + border: 1px dashed var(--edge-strong); + border-radius: 3px; + background: rgba(176, 129, 58, 0.08); } .padInner { @@ -199,7 +203,7 @@ min-width: 120px; height: 40px; border-radius: 2px; - background: var(--gold-leaf); + background: var(--brass); } .gapRow { @@ -213,7 +217,7 @@ .gapBox { height: 40px; border-radius: 2px; - background: var(--gold-leaf); + background: var(--brass); } // ---------------------------------------------------------------- controls @@ -226,17 +230,20 @@ .group { display: grid; gap: 1.1rem; - border: 1px solid rgba(107, 68, 35, 0.25); - border-radius: 3px; + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; padding: 1.4rem 1.5rem 1.6rem; - background: rgba(248, 236, 208, 0.28); + background: rgba(22, 17, 12, 0.45); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); } .groupLegend { font-family: var(--font-heading); - font-size: 1.05rem; - letter-spacing: 1px; - color: var(--ink-faded); + font-size: 0.95rem; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--brass); padding: 0 0.5rem; } @@ -255,32 +262,36 @@ .fieldLabel { font-family: var(--font-ui); - font-variant: small-caps; - font-size: 0.95rem; - letter-spacing: 1px; - color: var(--ink-faded); + font-weight: 500; + text-transform: uppercase; + font-size: 0.8rem; + letter-spacing: 0.14em; + color: var(--gaslight-dim); } .fieldHint { - font-family: var(--font-sans); - font-size: 0.75rem; + font-family: var(--font-body); + font-size: 0.8rem; font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-faint); } -// The canonical text control: the same parchment field used by the list -// search input, sized for regular forms. +// The canonical text control: a gunmetal well that takes a verdigris +// seal on focus. The list search input is the same field at display size. .control { appearance: none; -webkit-appearance: none; width: 100%; font-family: var(--font-ui); font-size: 1rem; + letter-spacing: 0.03em; padding: 0.6rem 0.75rem; - background: rgba(248, 236, 208, 0.55); - color: var(--ink); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: var(--well); + color: var(--gaslight); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.4); outline: none; transition: border-color 150ms ease, @@ -288,30 +299,33 @@ box-shadow 150ms ease; &::placeholder { - color: var(--ink-faded); - font-style: italic; + color: var(--gaslight-faint); } &:focus-visible { - border-color: var(--gold-leaf); - background: rgba(248, 236, 208, 0.9); - box-shadow: 0 0 0 2px rgba(212, 162, 89, 0.18); + border-color: rgba(121, 164, 140, 0.6); + background: rgba(15, 10, 5, 0.8); + box-shadow: + inset 0 1px 4px rgba(0, 0, 0, 0.4), + var(--focus-glow); } &:disabled { - opacity: 0.55; + opacity: 0.5; cursor: not-allowed; - background: rgba(248, 236, 208, 0.3); + background: rgba(15, 10, 5, 0.35); } &[readonly] { - background: rgba(248, 236, 208, 0.3); - color: var(--ink-faded); + background: rgba(15, 10, 5, 0.35); + color: var(--gaslight-dim); } &[aria-invalid="true"] { - border-color: var(--wax-seal); - box-shadow: 0 0 0 2px rgba(139, 26, 26, 0.14); + border-color: var(--ember); + box-shadow: + inset 0 1px 4px rgba(0, 0, 0, 0.4), + 0 0 0 2px rgba(208, 90, 62, 0.18); } @media (prefers-reduced-motion: reduce) { @@ -319,13 +333,13 @@ } } -// A select needs the hand-drawn chevron the native arrow can't match. +// A select needs the engraved chevron the native arrow can't match. select.control { cursor: pointer; padding-right: 2rem; background-image: - linear-gradient(45deg, transparent 50%, rgba(107, 68, 35, 0.55) 50%), - linear-gradient(135deg, rgba(107, 68, 35, 0.55) 50%, transparent 50%); + linear-gradient(45deg, transparent 50%, rgba(176, 129, 58, 0.75) 50%), + linear-gradient(135deg, rgba(176, 129, 58, 0.75) 50%, transparent 50%); background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.8rem) 55%; @@ -355,14 +369,14 @@ select.control { } option:checked { - background: rgba(212, 162, 89, 0.4); - color: var(--ink); + background: rgba(121, 164, 140, 0.2); + color: var(--gaslight); } } .range { width: 100%; - accent-color: var(--gold-leaf); + accent-color: var(--verdigris); cursor: pointer; } @@ -370,34 +384,39 @@ select.control { width: 100%; height: 3rem; padding: 0.2rem; - background: rgba(248, 236, 208, 0.55); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: var(--well); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; cursor: pointer; &:focus-visible { - border-color: var(--gold-leaf); + border-color: rgba(121, 164, 140, 0.6); outline: none; - box-shadow: 0 0 0 2px rgba(212, 162, 89, 0.18); + box-shadow: var(--focus-glow); } } .file { width: 100%; - font-family: var(--font-sans); + font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); + letter-spacing: 0.04em; + color: var(--gaslight-faint); &::file-selector-button { font-family: var(--font-ui); + font-weight: 500; font-size: 0.85rem; - letter-spacing: 0.5px; + text-transform: uppercase; + letter-spacing: 0.08em; margin-right: 0.75rem; padding: 0.45rem 0.9rem; - background: rgba(248, 236, 208, 0.6); - color: var(--ink); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: var(--plate); + color: var(--gaslight); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; cursor: pointer; transition: border-color 120ms ease, @@ -405,8 +424,8 @@ select.control { } &::file-selector-button:hover { - border-color: var(--gold-leaf); - color: var(--gold-leaf); + border-color: var(--edge-brass); + color: var(--brass-bright); } } @@ -415,18 +434,20 @@ select.control { .optionSet { display: grid; gap: 0.65rem; - border: 1px solid rgba(107, 68, 35, 0.3); - border-radius: 2px; + border: 1px solid var(--edge); + border-radius: var(--bevel-sm); + corner-shape: bevel; padding: 1rem 1.2rem 1.15rem; - background: rgba(248, 236, 208, 0.4); + background: rgba(18, 14, 9, 0.4); } .optionLegend { font-family: var(--font-ui); - font-variant: small-caps; - font-size: 0.9rem; - letter-spacing: 1px; - color: var(--ink-faded); + font-weight: 500; + text-transform: uppercase; + font-size: 0.8rem; + letter-spacing: 0.14em; + color: var(--gaslight-dim); padding: 0 0.4rem; } @@ -437,18 +458,18 @@ select.control { align-items: center; font-family: var(--font-body); font-size: 1.05rem; - color: var(--ink); + color: var(--gaslight); cursor: pointer; } .check { width: 1.15rem; height: 1.15rem; - accent-color: var(--gold-leaf); + accent-color: var(--verdigris); cursor: pointer; &:focus-visible { - outline: 2px solid var(--gold-leaf); + outline: 2px solid var(--verdigris); outline-offset: 2px; } } @@ -463,7 +484,7 @@ select.control { cursor: pointer; font-family: var(--font-body); font-size: 1.05rem; - color: var(--ink); + color: var(--gaslight); } // Visually hidden but focusable and in the tab order; the track/thumb render @@ -483,10 +504,11 @@ select.control { height: 26px; padding: 2px; border-radius: 13px; - border: 1px solid rgba(107, 68, 35, 0.35); - background: rgba(107, 68, 35, 0.28); + border: 1px solid var(--edge-strong); + background: rgba(15, 10, 5, 0.7); transition: background 150ms ease, + border-color 150ms ease, box-shadow 150ms ease; } @@ -494,14 +516,17 @@ select.control { width: 18px; height: 18px; border-radius: 50%; - background: var(--vellum); - box-shadow: 0 1px 2px rgba(61, 40, 23, 0.4); + background: var(--gaslight); + box-shadow: 0 1px 3px rgba(8, 5, 2, 0.6); transform: translateX(0); transition: transform 150ms ease; } +// Thrown on, the switch shows its patina. .switchInput:checked ~ .switchTrack { - background: var(--gold-leaf); + background: var(--verdigris-dim); + border-color: rgba(121, 164, 140, 0.5); + box-shadow: inset 0 0 8px rgba(121, 164, 140, 0.3); } .switchInput:checked ~ .switchTrack .switchThumb { @@ -509,8 +534,8 @@ select.control { } .switchInput:focus-visible ~ .switchTrack { - border-color: var(--gold-leaf); - box-shadow: 0 0 0 2px rgba(212, 162, 89, 0.35); + border-color: rgba(121, 164, 140, 0.6); + box-shadow: var(--focus-glow); } @media (prefers-reduced-motion: reduce) { @@ -538,23 +563,31 @@ select.control { .btn { appearance: none; font-family: var(--font-ui); - font-size: 0.95rem; - letter-spacing: 0.5px; + font-weight: 500; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.1em; padding: 0.6rem 1.3rem; - border-radius: 2px; - border: 1px solid rgba(107, 68, 35, 0.35); - background: rgba(248, 236, 208, 0.6); - color: var(--ink); + border-radius: var(--bevel-sm); + corner-shape: bevel; + border: 1px solid var(--edge-strong); + background: var(--plate); + color: var(--gaslight); cursor: pointer; transition: border-color 120ms ease, background 120ms ease, - color 120ms ease; + color 120ms ease, + box-shadow 120ms ease; + + &:hover:not(:disabled) { + border-color: var(--edge-brass); + color: var(--brass-bright); + } - &:hover:not(:disabled), &:focus-visible:not(:disabled) { - border-color: var(--gold-leaf); - color: var(--gold-leaf); + border-color: rgba(121, 164, 140, 0.6); + box-shadow: var(--focus-glow); outline: none; } @@ -568,22 +601,40 @@ select.control { } } +// The one loud control on the bench: a machined brass plate with an +// engraved label. .btnPrimary { @extend .btn; - background: var(--wax-seal); - border-color: var(--wax-seal); - color: var(--vellum); + font-weight: 600; + background: linear-gradient( + 180deg, + var(--brass-bright) 0%, + var(--brass) 55%, + #a87f30 100% + ); + border-color: var(--brass-dark); + color: #201503; + + &:hover:not(:disabled) { + filter: brightness(1.07); + border-color: var(--brass-dark); + color: #201503; + box-shadow: var(--brass-glow); + } - &:hover:not(:disabled), &:focus-visible:not(:disabled) { - color: var(--vellum); - border-color: var(--wax-seal); - filter: brightness(1.12); - box-shadow: 0 0 0 2px rgba(212, 162, 89, 0.3); + color: #201503; + box-shadow: var(--focus-glow); } } .btnGhost { @extend .btn; background: transparent; + border-color: rgba(176, 129, 58, 0.4); + color: var(--brass-bright); + + &:hover:not(:disabled) { + background: rgba(176, 129, 58, 0.08); + } } diff --git a/app/design/page.tsx b/app/design/page.tsx index 333754ab..82ce82d9 100644 --- a/app/design/page.tsx +++ b/app/design/page.tsx @@ -9,7 +9,7 @@ import styles from "@/app/design/page.module.scss"; export const metadata: Metadata = { title: "Design System · Atlas of the Known World", description: - "The visual grammar of the Known World: type, colour, spacing, and the full roll of form controls.", + "The visual grammar of the Known World: colour, type, spacing, and every control on the bench.", }; type Swatch = { token: string; hex: string; name: string }; @@ -18,32 +18,37 @@ type Swatch = { token: string; hex: string; name: string }; // tokens declared in `styles/globals.scss`. This page is the living catalogue // of those tokens; keep it in step when a token is added or renamed there. const CORE_PALETTE: Swatch[] = [ - { token: "--parchment-light", hex: "#f4e4c1", name: "Parchment Light" }, - { token: "--parchment-dark", hex: "#e8d3a0", name: "Parchment Dark" }, - { token: "--vellum", hex: "#f8ecd0", name: "Vellum" }, - { token: "--ink", hex: "#3d2817", name: "Ink" }, - { token: "--ink-faded", hex: "#6b4423", name: "Ink Faded" }, - { token: "--ink-slate", hex: "#2e3a55", name: "Ink Slate" }, - { token: "--ink-rose", hex: "#6a3142", name: "Ink Rose" }, - { token: "--wax-seal", hex: "#8b1a1a", name: "Wax Seal" }, - { token: "--gold-leaf", hex: "#d4a259", name: "Gold Leaf" }, - { token: "--rose-madder", hex: "#a85a6b", name: "Rose Madder" }, + { token: "--coal", hex: "#14100a", name: "Coal" }, + { token: "--iron", hex: "#1d1712", name: "Iron" }, + { token: "--gunmetal", hex: "#271f16", name: "Gunmetal" }, + { token: "--gunmetal-raised", hex: "#322818", name: "Raised Gunmetal" }, + { token: "--steel", hex: "#4a3b2b", name: "Steel" }, + { token: "--brass", hex: "#b0813a", name: "Brass" }, + { token: "--brass-bright", hex: "#e4bf78", name: "Bright Brass" }, + { token: "--brass-dark", hex: "#77571f", name: "Dark Brass" }, + { token: "--verdigris", hex: "#79a48c", name: "Verdigris" }, + { token: "--verdigris-dim", hex: "#435f4e", name: "Deep Verdigris" }, + { token: "--copper", hex: "#c1704a", name: "Copper" }, + { token: "--gaslight", hex: "#f0e7d2", name: "Gaslight" }, + { token: "--gaslight-dim", hex: "#bfb096", name: "Dim Gaslight" }, + { token: "--gaslight-faint", hex: "#8f8069", name: "Faint Gaslight" }, + { token: "--ember", hex: "#d05a3e", name: "Ember" }, ]; const REGION_PALETTE: Swatch[] = [ - { token: "--region-color-north", hex: "#5d6f7f", name: "North" }, - { token: "--region-color-vale", hex: "#4d7caa", name: "Vale" }, - { token: "--region-color-riverlands", hex: "#3d5a7c", name: "Riverlands" }, - { token: "--region-color-westerlands", hex: "#b13a3a", name: "Westerlands" }, - { token: "--region-color-reach", hex: "#6f8336", name: "Reach" }, - { token: "--region-color-stormlands", hex: "#c89a31", name: "Stormlands" }, - { token: "--region-color-dorne", hex: "#c7693a", name: "Dorne" }, + { token: "--region-color-north", hex: "#92a6b0", name: "North" }, + { token: "--region-color-vale", hex: "#7ba3c4", name: "Vale" }, + { token: "--region-color-riverlands", hex: "#6b89a8", name: "Riverlands" }, + { token: "--region-color-westerlands", hex: "#c25c50", name: "Westerlands" }, + { token: "--region-color-reach", hex: "#93a854", name: "Reach" }, + { token: "--region-color-stormlands", hex: "#d3a94e", name: "Stormlands" }, + { token: "--region-color-dorne", hex: "#cd7f4b", name: "Dorne" }, { token: "--region-color-iron-islands", - hex: "#2f4a44", + hex: "#6d9186", name: "Iron Islands", }, - { token: "--region-color-crownlands", hex: "#5a2a30", name: "Crownlands" }, + { token: "--region-color-crownlands", hex: "#ad5c62", name: "Crownlands" }, ]; type FontRole = { family: string; label: string; sample: string }; @@ -51,22 +56,22 @@ type FontRole = { family: string; label: string; sample: string }; const FONT_ROLES: FontRole[] = [ { family: "var(--font-heading)", - label: "Cinzel · Headings", + label: "Bevan · Headings", sample: "The Known World", }, { family: "var(--font-character-name)", - label: "Cormorant Unicase · Character names", + label: "Graduate · Nameplates", sample: "Eddard Stark", }, { family: "var(--font-body)", - label: "EB Garamond · Body & UI", + label: "Old Standard TT · Body", sample: "The night is dark and full of terrors.", }, { family: "var(--font-sans)", - label: "Alegreya Sans · Data & captions", + label: "Oswald · Labels & data", sample: "298 AC · 42 houses · 9 regions", }, ]; @@ -210,7 +215,7 @@ export default function DesignSystemPage() { {/* ---------------------------------------------------------------- */} @@ -265,21 +270,22 @@ export default function DesignSystemPage() {

Typography

- Headings are set in Cinzel; body copy in EB Garamond. The scale below - renders each element exactly as the global stylesheet styles it. + Headings are struck in Bevan; body copy is set in Old Standard TT. The + scale below renders each element exactly as the global stylesheet + styles it.

- H1 · 2.2rem + H1 · 2rem

Winter Is Coming

- H2 · 1.4rem + H2 · 1.2rem

The Great Houses

- H3 · 1.15rem + H3 · 0.9rem

Wardens of the North

@@ -304,8 +310,8 @@ export default function DesignSystemPage() {

Words can carry bold weight or an{" "} italic aside, and a{" "} - cross-reference links onward in ink - and gold. + cross-reference links onward in brass + and verdigris.

@@ -380,9 +386,9 @@ export default function DesignSystemPage() {

Form controls

- Every native control, dressed in parchment and ink. Try them: the - fields hold their own state, and Reset fields restores the - defaults. + Every native control, machined in gunmetal and brass and sealed with + verdigris. Try them: the fields hold their own state, and{" "} + Reset fields restores the defaults.

@@ -441,7 +447,7 @@ export default function DesignSystemPage() { id="df-color" className={styles.colorInput} type="color" - defaultValue="#8b1a1a" + defaultValue="#79a48c" /> diff --git a/app/dragons/[slug]/page.module.scss b/app/dragons/[slug]/page.module.scss index e9e7deb0..75843354 100644 --- a/app/dragons/[slug]/page.module.scss +++ b/app/dragons/[slug]/page.module.scss @@ -59,14 +59,15 @@ padding-bottom: 3rem; font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/app/events/[slug]/page.module.scss b/app/events/[slug]/page.module.scss index b0058306..b3790170 100644 --- a/app/events/[slug]/page.module.scss +++ b/app/events/[slug]/page.module.scss @@ -48,14 +48,15 @@ padding-bottom: 3rem; font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/app/houses/[slug]/page.module.scss b/app/houses/[slug]/page.module.scss index 954a36b8..aa7e3d4a 100644 --- a/app/houses/[slug]/page.module.scss +++ b/app/houses/[slug]/page.module.scss @@ -40,12 +40,11 @@ h1 { font-family: var(--font-character-name); + text-transform: none; font-size: 2.7rem; - letter-spacing: 0.04em; + letter-spacing: 0.05em; line-height: 1.05; - text-shadow: - 0 1px 0 rgba(255, 250, 235, 0.5), - 0 2px 5px rgba(61, 40, 23, 0.12); + text-shadow: 0 2px 10px rgba(8, 5, 2, 0.6); @media (max-width: bp.$sm) { font-size: 2.1rem; @@ -57,7 +56,7 @@ padding-bottom: 0; font-size: 1.2rem; letter-spacing: 0.02em; - color: var(--ink-faded); + color: var(--gaslight-dim); } } @@ -116,9 +115,9 @@ li { padding: 0.4rem 0.75rem; - background: rgba(248, 236, 208, 0.6); - border: 1px solid rgba(107, 68, 35, 0.15); - border-radius: 2px; + background: rgba(22, 17, 12, 0.55); + border: 1px solid var(--edge); + border-radius: 3px; line-height: 1.45; } } @@ -126,7 +125,7 @@ .notableName { font-family: var(--font-body); font-weight: 600; - color: var(--ink); + color: var(--gaslight); } a.notableName { @@ -135,9 +134,9 @@ a.notableName { &:hover, &:focus-visible { - color: var(--gold-leaf); + color: var(--verdigris); text-decoration: underline; - text-decoration-color: rgba(212, 162, 89, 0.45); + text-decoration-color: rgba(121, 164, 140, 0.45); outline: none; } @@ -148,7 +147,7 @@ a.notableName { .notableNote { font-family: var(--font-body); - color: var(--ink-faded); + color: var(--gaslight-dim); } .back { @@ -156,14 +155,15 @@ a.notableName { padding-bottom: 3rem; font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/app/layout.tsx b/app/layout.tsx index 0f57b32c..c821f7fd 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,35 +1,31 @@ import type { Metadata, Viewport } from "next"; -import { - Alegreya_Sans, - Cinzel, - Cormorant_Unicase, - EB_Garamond, -} from "next/font/google"; +import { Bevan, Graduate, Old_Standard_TT, Oswald } from "next/font/google"; import { NuqsAdapter } from "nuqs/adapters/next/app"; import "../styles/globals.scss"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteMenu } from "@/components/SiteMenu"; import { SiteFooter } from "@/components/SiteFooter"; -const cinzel = Cinzel({ +const bevan = Bevan({ subsets: ["latin"], - variable: "--font-cinzel", - weight: ["400", "600", "700"], + variable: "--font-bevan", + weight: "400", }); -const cormorantUnicase = Cormorant_Unicase({ +const graduate = Graduate({ subsets: ["latin"], - variable: "--font-cormorant-unicase", - weight: ["500", "600", "700"], + variable: "--font-graduate", + weight: "400", }); -const ebGaramond = EB_Garamond({ +const oldStandard = Old_Standard_TT({ subsets: ["latin"], - variable: "--font-eb-garamond", - weight: ["400", "500", "600"], + variable: "--font-old-standard", + weight: ["400", "700"], + style: ["normal", "italic"], }); -const alegreyaSans = Alegreya_Sans({ +const oswald = Oswald({ subsets: ["latin"], - variable: "--font-alegreya-sans", - weight: ["400", "500", "700"], + variable: "--font-oswald", + weight: ["400", "500", "600"], }); export const metadata: Metadata = { @@ -54,7 +50,7 @@ export default function RootLayout({ return ( diff --git a/app/page.module.scss b/app/page.module.scss new file mode 100644 index 00000000..62d29b08 --- /dev/null +++ b/app/page.module.scss @@ -0,0 +1,75 @@ +@use "breakpoints" as bp; + +// The homepage spread: masthead column on the left, contents ledger on the +// right, like the title page of a bound atlas facing its index. +.contents { + display: grid; + grid-template-areas: "masthead ledger"; + grid-template-columns: minmax(240px, 320px) minmax(0, 1fr); + gap: 3.5rem; + align-items: start; + width: 100%; + max-width: 1080px; + padding-top: 2.5rem; + + > :last-child { + grid-area: ledger; + } + + @media (max-width: bp.$lg) { + grid-template-areas: + "masthead" + "ledger"; + grid-template-columns: minmax(0, 1fr); + gap: 2.5rem; + max-width: 720px; + padding-top: 1rem; + } +} + +.masthead { + grid-area: masthead; + display: grid; + justify-items: start; + gap: 1rem; + position: sticky; + top: 7.5rem; + + @media (max-width: bp.$lg) { + position: static; + justify-items: center; + text-align: center; + gap: 0.85rem; + } +} + +.eyebrow { + font-family: var(--font-ui); + font-weight: 400; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.32em; + color: var(--brass); +} + +.title { + text-align: left; + font-size: 2.4rem; + line-height: 1.15; + + @media (max-width: bp.$lg) { + text-align: center; + font-size: 2rem; + } +} + +.rule { + width: min(260px, 100%); +} + +.lede { + font-style: italic; + font-size: 1.1rem; + color: var(--gaslight-dim); + max-width: 34ch; +} diff --git a/app/page.tsx b/app/page.tsx index 14d61548..86e3a239 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,11 +1,23 @@ import { ParchmentLayout } from "@/components/ParchmentLayout"; import { MainMenu } from "@/components/MainMenu"; +import { FiligreeRule } from "@/components/Filigree"; +import styles from "@/app/page.module.scss"; export default function Home() { return ( -

Choose a path.

- +
+
+

A Song of Ice and Fire

+

Table of Plates

+ +

+ The realm surveyed from the Wall to the Summer Sea, engraved plate + by plate. Open one to begin. +

+
+ +
); } diff --git a/app/timeline/page.module.scss b/app/timeline/page.module.scss index b653c6f8..714ca8d7 100644 --- a/app/timeline/page.module.scss +++ b/app/timeline/page.module.scss @@ -9,10 +9,11 @@ margin-top: 2rem; font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); - font-style: italic; + color: var(--gaslight-faint); + letter-spacing: 0.04em; @media (max-width: bp.$md) { padding-left: 1.5rem; + padding-right: 1.5rem; } } diff --git a/app/weapons/[slug]/page.module.scss b/app/weapons/[slug]/page.module.scss index 98b31bd8..58751630 100644 --- a/app/weapons/[slug]/page.module.scss +++ b/app/weapons/[slug]/page.module.scss @@ -57,9 +57,12 @@ display: grid; width: 100%; overflow: hidden; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 4px; - box-shadow: 0 2px 12px rgba(40, 25, 10, 0.12); + border: 1px solid var(--edge-brass); + border-radius: var(--bevel); + corner-shape: bevel; + box-shadow: + inset 0 0 30px rgba(8, 5, 2, 0.35), + var(--shadow-soft); margin: 0; img { @@ -85,14 +88,15 @@ padding-bottom: 3rem; font-family: var(--font-ui); font-size: 0.9rem; + letter-spacing: 0.06em; text-align: center; a { - color: var(--ink-faded); + color: var(--gaslight-faint); text-decoration: none; &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } } diff --git a/components/Accordion/Accordion.module.scss b/components/Accordion/Accordion.module.scss index 5c29f859..66989308 100644 --- a/components/Accordion/Accordion.module.scss +++ b/components/Accordion/Accordion.module.scss @@ -1,7 +1,8 @@ .accordion { - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - background: rgba(248, 236, 208, 0.35); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + background: rgba(18, 14, 9, 0.5); overflow: hidden; } @@ -18,10 +19,10 @@ align-items: center; gap: 0.75rem; padding: 0.9rem 1.1rem; - background: rgba(248, 236, 208, 0.55); + background: var(--plate); border: 0; cursor: pointer; - color: var(--ink); + color: var(--gaslight); font-family: var(--font-character-name); font-size: 1.3rem; text-align: left; @@ -31,23 +32,31 @@ &:hover, &:focus-visible { - background: rgba(248, 236, 208, 0.9); - color: var(--gold-leaf); + background: linear-gradient(180deg, #4a3b2b 0%, var(--gunmetal) 100%); + color: var(--brass-bright); outline: none; } + + @media (prefers-reduced-motion: reduce) { + transition: none; + } } .chevron { - color: var(--ink-faded); - transition: transform 150ms ease; + color: var(--gaslight-faint); + transition: + transform 150ms ease, + color 150ms ease; @media (prefers-reduced-motion: reduce) { transition: none; } } +// An open drawer is a live circuit: the chevron turns and charges. .chevronOpen { transform: rotate(90deg); + color: var(--verdigris); } .title { @@ -56,16 +65,17 @@ .count { font-family: var(--font-ui); - font-size: 0.9rem; - color: var(--ink-faded); - font-variant: small-caps; - letter-spacing: 1px; + font-size: 0.8rem; + font-weight: 500; + color: var(--gaslight-faint); + text-transform: uppercase; + letter-spacing: 0.12em; } .panel { - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: 1fr; gap: 1rem; padding: 1rem 1.1rem 1.25rem; - border-top: 1px solid rgba(107, 68, 35, 0.2); + border-top: 1px solid var(--edge); } diff --git a/components/CharacterSearchInput/CharacterSearchInput.module.scss b/components/CharacterSearchInput/CharacterSearchInput.module.scss index ffe87173..1cacfd70 100644 --- a/components/CharacterSearchInput/CharacterSearchInput.module.scss +++ b/components/CharacterSearchInput/CharacterSearchInput.module.scss @@ -26,26 +26,29 @@ gap: 0.15rem; max-height: 18rem; overflow-y: auto; - background: rgba(248, 236, 208, 0.97); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - box-shadow: 0 8px 24px rgba(31, 18, 8, 0.22); + background: #2b2217; + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: var(--shadow-raised); } .option { - display: flex; + display: grid; + grid-template-columns: auto 1fr; align-items: baseline; gap: 0.5rem; padding: 0.55rem 0.7rem; border-radius: 2px; cursor: pointer; - color: var(--ink); + color: var(--gaslight); font-family: var(--font-character-name); font-size: 1.4rem; } +// The keyboard-active row runs charged. .optionActive { - background: rgba(212, 162, 89, 0.22); + background: rgba(121, 164, 140, 0.14); } .optionName { @@ -56,5 +59,5 @@ font-family: var(--font-body); font-style: italic; font-size: 0.95rem; - color: var(--ink-faded); + color: var(--gaslight-dim); } diff --git a/components/ComingSoonPage/ComingSoonPage.module.scss b/components/ComingSoonPage/ComingSoonPage.module.scss index 873096d1..fb1cd279 100644 --- a/components/ComingSoonPage/ComingSoonPage.module.scss +++ b/components/ComingSoonPage/ComingSoonPage.module.scss @@ -2,12 +2,13 @@ text-align: center; font-family: var(--font-ui); font-size: 0.8rem; - letter-spacing: 1px; + font-weight: 500; + letter-spacing: 0.16em; text-transform: uppercase; - color: var(--ink-faded); + color: var(--gaslight-faint); } .caption a { text-decoration: none; - color: var(--ink-faded); + color: var(--gaslight-faint); } diff --git a/components/DropCap/DropCap.module.scss b/components/DropCap/DropCap.module.scss index 7aab0b44..b8532f6e 100644 --- a/components/DropCap/DropCap.module.scss +++ b/components/DropCap/DropCap.module.scss @@ -3,6 +3,6 @@ font-size: 3.2rem; line-height: 1; padding: 0.15rem 0.5rem 0 0; - color: var(--ink-faded); - font-weight: 600; + color: var(--brass); + font-weight: 400; } diff --git a/components/FamilyTree/FamilyTree.module.scss b/components/FamilyTree/FamilyTree.module.scss index 9150d777..9485c955 100644 --- a/components/FamilyTree/FamilyTree.module.scss +++ b/components/FamilyTree/FamilyTree.module.scss @@ -23,13 +23,14 @@ display: grid; gap: 0.2rem; + // Descent lines are pipework: cold steel rails between generations. &::before { content: ""; position: absolute; left: 0.4rem; top: 0; bottom: 0; - border-left: 1px solid rgba(107, 68, 35, 0.35); + border-left: 1px solid var(--edge-strong); } &::after { @@ -38,7 +39,7 @@ left: 0.4rem; top: 0.85rem; width: 1rem; - border-top: 1px solid rgba(107, 68, 35, 0.35); + border-top: 1px solid var(--edge-strong); } &:last-child::before { @@ -56,29 +57,35 @@ } .row { - display: flex; + display: grid; + grid-auto-flow: column; + grid-auto-columns: max-content; + justify-content: start; align-items: baseline; - flex-wrap: wrap; gap: 0.4rem 0.6rem; padding: 0.25rem 0.6rem; width: fit-content; max-width: 100%; - background: rgba(248, 236, 208, 0.6); - border: 1px solid rgba(107, 68, 35, 0.15); - border-radius: 2px; + background: rgba(22, 17, 12, 0.55); + border: 1px solid var(--edge); + border-radius: 3px; @media (max-width: bp.$sm) { + grid-auto-flow: row; + justify-items: start; padding: 0.4rem 0.6rem; } } .rowPlaceholder { background: transparent; - border: 1px dotted rgba(107, 68, 35, 0.55); + border: 1px dotted var(--edge-strong); } .person { - display: flex; + display: grid; + grid-auto-flow: column; + grid-auto-columns: max-content; align-items: baseline; gap: 0.45rem; } @@ -86,7 +93,7 @@ .name { font-family: var(--font-body); font-size: 1rem; - color: var(--ink); + color: var(--gaslight); font-weight: 500; } @@ -97,9 +104,9 @@ a.name { &:hover, &:focus-visible { - color: var(--gold-leaf); + color: var(--verdigris); text-decoration: underline; - text-decoration-color: rgba(212, 162, 89, 0.45); + text-decoration-color: rgba(121, 164, 140, 0.45); outline: none; } @@ -109,29 +116,30 @@ a.name { } .nameM { - color: var(--ink-slate); + color: var(--tint-male); } .nameF { - color: var(--ink-rose); + color: var(--tint-female); } .nameExternal { - color: var(--ink-faded); + color: var(--gaslight-dim); font-style: italic; } .namePlaceholder { - color: var(--ink-faded); + color: var(--gaslight-dim); font-style: italic; opacity: 0.8; } .lifespan { font-family: var(--font-ui); - font-size: 0.72rem; - color: var(--ink-faded); - letter-spacing: 0.5px; + font-size: 0.75rem; + color: var(--gaslight-faint); + letter-spacing: 0.05em; + font-variant-numeric: tabular-nums; } .alias { @@ -139,17 +147,19 @@ a.name { font-style: italic; font-weight: 400; font-size: 0.78rem; - color: var(--ink-faded); + color: var(--gaslight-dim); } .spouse { - display: flex; + display: grid; + grid-auto-flow: column; + grid-auto-columns: max-content; align-items: baseline; gap: 0.3rem; } .cross { - color: var(--gold-leaf); + color: var(--brass); font-size: 0.95rem; } @@ -159,22 +169,22 @@ a.name { } .genderM { - color: var(--gold-leaf); + color: var(--tint-male); } .genderF { - color: var(--rose-madder); + color: var(--tint-female); } .king { font-size: 0.85rem; - color: var(--gold-leaf); + color: var(--brass-bright); padding-right: 0.15rem; } .empty { font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); text-align: center; padding: 2rem 1rem; } diff --git a/components/FamilyTreeChart/FamilyTreeChart.module.scss b/components/FamilyTreeChart/FamilyTreeChart.module.scss index 0df23dc6..df072b19 100644 --- a/components/FamilyTreeChart/FamilyTreeChart.module.scss +++ b/components/FamilyTreeChart/FamilyTreeChart.module.scss @@ -3,9 +3,10 @@ width: 100%; height: clamp(500px, 75vh, 900px); overflow: hidden; - border: 1px solid rgba(107, 68, 35, 0.25); - border-radius: 2px; - background: rgba(248, 236, 208, 0.45); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + background: rgba(18, 14, 9, 0.5); touch-action: none; user-select: none; } @@ -22,8 +23,8 @@ } .dot { - fill: rgba(248, 236, 208, 0.85); - stroke: rgba(107, 68, 35, 0.6); + fill: var(--gunmetal-raised); + stroke: rgba(190, 158, 110, 0.65); stroke-width: 1.5; } @@ -39,26 +40,28 @@ .label { font-family: var(--font-ui); font-size: 9px; - fill: var(--ink-faded); + fill: var(--gaslight-dim); text-anchor: middle; pointer-events: none; } +// Solid backing that approximates the canvas colour so labels mask the +// edges passing beneath them. .labelBg { - fill: #f8ecd0; + fill: #1b1510; pointer-events: none; } .cross { font-family: var(--font-body); font-size: 10px; - fill: var(--gold-leaf); + fill: var(--brass); text-anchor: middle; pointer-events: none; } .edge { - stroke: rgba(107, 68, 35, 0.5); + stroke: rgba(190, 158, 110, 0.4); stroke-width: 1; fill: none; } @@ -69,7 +72,7 @@ width: 100%; height: 100%; font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); padding: 2rem 1rem; text-align: center; } @@ -81,15 +84,17 @@ display: grid; gap: 0.25rem; padding: 0.4rem; - background: rgba(248, 236, 208, 0.92); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: rgba(27, 21, 16, 0.92); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; font-family: var(--font-ui); z-index: 1; } .controlRow { - display: flex; + display: grid; + grid-auto-flow: column; gap: 0.2rem; justify-content: center; } @@ -97,28 +102,31 @@ .controlButton { appearance: none; background: transparent; - border: 1px solid rgba(107, 68, 35, 0.25); + border: 1px solid var(--edge); border-radius: 2px; font-size: 0.7rem; + letter-spacing: 0.04em; padding: 0.15rem 0.4rem; min-width: 1.5rem; - color: var(--ink-faded); + color: var(--gaslight-dim); cursor: pointer; transition: background 120ms ease, - color 120ms ease; + color 120ms ease, + border-color 120ms ease; &:hover, &:focus-visible { - color: var(--gold-leaf); - background: rgba(212, 162, 89, 0.18); + color: var(--brass-bright); + background: rgba(176, 129, 58, 0.1); outline: none; } + // The engaged mode runs charged. &[aria-pressed="true"] { - color: var(--ink); - background: rgba(212, 162, 89, 0.28); - border-color: rgba(212, 162, 89, 0.55); + color: var(--verdigris); + background: rgba(121, 164, 140, 0.12); + border-color: rgba(121, 164, 140, 0.45); } @media (prefers-reduced-motion: reduce) { @@ -138,5 +146,5 @@ z-index: 1000; border: 0; border-radius: 0; - background: rgb(248, 236, 208); + background: var(--iron); } diff --git a/components/Filigree/Filigree.module.scss b/components/Filigree/Filigree.module.scss index 17a3efc1..f22fc777 100644 --- a/components/Filigree/Filigree.module.scss +++ b/components/Filigree/Filigree.module.scss @@ -1,9 +1,9 @@ .flourish { display: grid; align-items: center; - color: var(--gold-leaf); + color: var(--brass); line-height: 0; - opacity: 0.85; + opacity: 0.9; pointer-events: none; svg { @@ -20,9 +20,9 @@ .rule { display: grid; justify-items: center; - color: var(--gold-leaf); + color: var(--brass); line-height: 0; - opacity: 0.9; + opacity: 0.95; pointer-events: none; -webkit-mask-image: linear-gradient( 90deg, @@ -45,3 +45,31 @@ height: auto; } } + +// The gauge needle wanders its dial like a boiler under uneven draught. +// The one moving part of the whole page furniture. +.needle { + color: var(--brass-bright); + transform-origin: 120px 12px; + transform-box: view-box; + animation: needle-drift 9s ease-in-out infinite alternate; + + @media (prefers-reduced-motion: reduce) { + animation: none; + } +} + +// The hex nut that caps the flourish's pipework. +.nut { + fill: currentColor; + opacity: 0.9; +} + +@keyframes needle-drift { + 0% { + transform: rotate(-14deg); + } + 100% { + transform: rotate(30deg); + } +} diff --git a/components/Filigree/Filigree.tsx b/components/Filigree/Filigree.tsx index 3c1bb6d3..dea2ddbe 100644 --- a/components/Filigree/Filigree.tsx +++ b/components/Filigree/Filigree.tsx @@ -6,6 +6,9 @@ type FlourishProps = { className?: string; }; +// A short run of pipework: an engraved rail feeding a cogwheel, closed off +// with a hex nut. Reads left to right; `mirrored` flips it for the +// right-hand side. export function FiligreeFlourish({ mirrored = false, className, @@ -16,17 +19,24 @@ export function FiligreeFlourish({ aria-hidden="true" > - - - - + + + + + + + + + + + + + ); @@ -36,18 +46,41 @@ type RuleProps = { className?: string; }; +// The atlas signature: a pressure gauge set into an engraved rail, its +// needle wandering the dial. It underlines every page title and divides +// long pages. export function FiligreeRule({ className }: RuleProps) { return ( ); diff --git a/components/FilteredCharacterList/FilteredCharacterList.module.scss b/components/FilteredCharacterList/FilteredCharacterList.module.scss index 9076d9e2..ac77ebe1 100644 --- a/components/FilteredCharacterList/FilteredCharacterList.module.scss +++ b/components/FilteredCharacterList/FilteredCharacterList.module.scss @@ -2,7 +2,7 @@ // `ParchmentLayout`'s `.page` uses `justify-items: center` so the `h1` // and `.subtitle` size to their content. `justify-self: stretch` opts -// this grid back out so it fills the full parchment column. +// this grid back out so it fills the full page column. .list { list-style: none; padding: 0; @@ -14,7 +14,7 @@ } // Make the `
  • ` a single-cell grid so the inner `` stretches to -// fill the row's auto height — otherwise the tallest card in a row +// fill the row's auto height; otherwise the tallest card in a row // (one with an alias) leaves its shorter neighbours visually short. .item { margin: 0; @@ -29,21 +29,24 @@ "name" "alias"; align-content: start; - background: var(--vellum); - border: 2px solid rgba(107, 68, 35, 0.25); - border-radius: 2px; - color: var(--ink); + background: var(--plate); + border: 2px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + color: var(--gaslight); text-decoration: none; text-align: center; overflow: hidden; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-soft); transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease; } -// Regional border tints — colours live in `globals.scss` as +// Regional border tints; colours live in `globals.scss` as // `--region-color-*` tokens (shared with `FilteredHouseList`). .cardNorth { border-color: var(--region-color-north); @@ -76,10 +79,13 @@ a.card { &:hover, &:focus-visible { - border-color: var(--gold-leaf); - transform: scale(1.01); - box-shadow: 0 2px 12px rgba(212, 162, 89, 0.18); - color: var(--ink); + border-color: var(--brass-bright); + transform: translateY(-2px); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-raised), + var(--brass-glow); + color: var(--gaslight); outline: none; } } @@ -100,8 +106,7 @@ a.card { // Sigil overlays the top-right corner of the portrait in grid view. // Both `.portrait` and `.sigil` claim `grid-area: portrait`, layering // the sigil over the image; `place-self` parks it in the corner. -// `aspect-ratio: 1` keeps the badge square even if flex would otherwise -// shrink its main axis. +// `aspect-ratio: 1` keeps the badge square regardless of intrinsic size. .sigil { grid-area: portrait; place-self: start end; @@ -117,23 +122,23 @@ a.card { .name { grid-area: name; font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 2px; - font-size: 1.05rem; - font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 1rem; + font-weight: 400; line-height: 1.2; - padding: 1rem 0; + padding: 1rem 0.5rem; } .alias { grid-area: alias; font-family: var(--font-body); - font-variant: normal; + text-transform: none; font-style: italic; font-weight: 400; letter-spacing: 0; font-size: 0.85rem; - color: var(--ink-faded); + color: var(--gaslight-dim); padding: 0; min-height: 2.5rem; align-content: center; @@ -147,7 +152,10 @@ a.card { --bleed-max: 1280px; .card { - display: flex; + display: grid; + grid-auto-flow: column; + grid-auto-columns: max-content; + justify-content: start; align-items: center; gap: 0.85rem; min-height: 5.75rem; @@ -156,7 +164,7 @@ a.card { border-width: 1px; @media (max-width: bp.$sm) { - display: grid; + grid-auto-flow: row; grid-template-columns: auto 1fr; grid-template-areas: "portrait name" @@ -174,9 +182,8 @@ a.card { } .portrait { - flex: 0 0 auto; - display: flex; - align-items: center; + display: grid; + align-content: center; img { height: 5.75rem; @@ -192,7 +199,6 @@ a.card { } .sigil { - flex: 0 0 3.25rem; place-self: auto; width: 3.25rem; aspect-ratio: auto; @@ -209,7 +215,7 @@ a.card { .name { padding: 0; - font-size: 1.1rem; + font-size: 1.05rem; @media (max-width: bp.$sm) { grid-area: name; diff --git a/components/FilteredDragonList/FilteredDragonList.module.scss b/components/FilteredDragonList/FilteredDragonList.module.scss index 5ad5521a..5412d9ef 100644 --- a/components/FilteredDragonList/FilteredDragonList.module.scss +++ b/components/FilteredDragonList/FilteredDragonList.module.scss @@ -1,6 +1,6 @@ // `ParchmentLayout`'s `.page` uses `justify-items: center` so the `h1` // and `.subtitle` size to their content. `justify-self: stretch` opts -// this grid back out so it fills the full parchment column. +// this grid back out so it fills the full page column. .list { list-style: none; padding: 0; @@ -13,34 +13,41 @@ .item { margin: 0; + display: grid; } .card { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; + display: grid; + grid-template-columns: 1fr; + justify-items: center; + align-content: center; gap: 0.85rem; min-height: 10.5rem; padding: 1rem; - background: var(--vellum); - border: 2px solid rgba(107, 68, 35, 0.25); - border-radius: 2px; - color: var(--ink); + background: var(--plate); + border: 2px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + color: var(--gaslight); text-decoration: none; text-align: center; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-soft); transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04); &:hover, &:focus-visible { - border-color: var(--gold-leaf); - transform: scale(1.01); - box-shadow: 0 2px 12px rgba(212, 162, 89, 0.18); - color: var(--ink); + border-color: var(--brass-bright); + transform: translateY(-2px); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-raised), + var(--brass-glow); + color: var(--gaslight); } @media (prefers-reduced-motion: reduce) { @@ -53,7 +60,7 @@ } } -// Regional border tints — colours live in `globals.scss` as +// Regional border tints; colours live in `globals.scss` as // `--region-color-*` tokens (shared with `FilteredCharacterList`). .cardNorth { border-color: var(--region-color-north); @@ -85,26 +92,28 @@ .name { font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 2px; - font-size: 1.2rem; - font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 1.1rem; + font-weight: 400; } +// Wild dragons answer to no house: they carry raw copper, the warm +// accent reserved for the unbound. .cardWild { - border-color: var(--ink-faded); + border-color: rgba(193, 112, 74, 0.45); } .wildBadge { - display: inline-flex; - align-items: center; - justify-content: center; + display: inline-grid; + place-items: center; width: 6rem; height: 6rem; font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 2px; - color: var(--ink-faded); - border: 1px dashed rgba(107, 68, 35, 0.35); + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.85rem; + color: var(--copper); + border: 1px dashed rgba(193, 112, 74, 0.5); border-radius: 50%; } diff --git a/components/FilteredHouseList/FilteredHouseList.module.scss b/components/FilteredHouseList/FilteredHouseList.module.scss index 19e39182..971842c1 100644 --- a/components/FilteredHouseList/FilteredHouseList.module.scss +++ b/components/FilteredHouseList/FilteredHouseList.module.scss @@ -1,33 +1,50 @@ @use "breakpoints" as bp; +// The shared `listSearch.input` pins itself to a `search` grid area, so the +// controls grid names its tracks rather than relying on auto-placement. .controls { - display: flex; - flex-wrap: wrap; + display: grid; + grid-template-columns: minmax(16rem, 1fr) auto; + grid-template-areas: "search toggles"; align-items: center; gap: 1rem; width: 100%; + + @media (max-width: bp.$md) { + grid-template-columns: 1fr; + grid-template-areas: + "search" + "toggles"; + justify-items: stretch; + } } .searchInput { - flex: 1 1 16rem; min-width: 0; } .toggles { - display: flex; + grid-area: toggles; + display: grid; + grid-auto-flow: column; align-items: center; gap: 1rem; - margin-left: auto; + justify-self: end; + + @media (max-width: bp.$md) { + justify-self: center; + } } .rankFilter { - display: flex; + display: grid; + grid-auto-flow: column; align-items: center; gap: 0.4rem; font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); - letter-spacing: 0.5px; + color: var(--gaslight-faint); + letter-spacing: 0.06em; // Match the 50px height of the sibling `SortToggle` / `ViewToggle` // buttons and enlarge the option text. Scoped here so the shared @@ -40,8 +57,8 @@ } .rankLabel { - font-variant: small-caps; - letter-spacing: 1.5px; + text-transform: uppercase; + letter-spacing: 0.12em; } .regions { @@ -51,6 +68,9 @@ } .regionSearch { + // The shared `listSearch.input` claims `grid-area: search`; inside the + // accordion panel's plain grid that area does not exist, so opt back out. + grid-area: auto; font-size: 1.1rem; padding: 0.6rem 0.75rem; @@ -61,7 +81,7 @@ // `ParchmentLayout`'s `.page` uses `justify-items: center` so the `h1` // and `.subtitle` size to their content. `justify-self: stretch` opts -// this grid back out so it fills the full parchment column. +// this grid back out so it fills the full page column. .list { list-style: none; padding: 0; @@ -94,35 +114,41 @@ .item { margin: 0; + display: grid; } .card { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; + display: grid; + grid-template-columns: 1fr; + justify-items: center; + align-content: center; gap: 0; min-height: 10.5rem; padding: 0.5rem; - background: var(--vellum); - border: 2px solid white; - border-radius: 2px; - color: var(--ink); + background: var(--plate); + border: 2px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + color: var(--gaslight); text-decoration: none; text-align: center; - border-radius: 8px; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-soft); transition: border-color 150ms ease, transform 150ms ease, box-shadow 150ms ease; - box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); &:hover, &:focus-visible { - border-color: white; - transform: scale(1.01); - box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.25); - color: var(--ink); + border-color: var(--brass-bright); + transform: translateY(-2px); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-raised), + var(--brass-glow); + color: var(--gaslight); } @media (prefers-reduced-motion: reduce) { @@ -135,8 +161,10 @@ } } -// Regional border tints — colours live in `globals.scss` as +// Regional border tints; colours live in `globals.scss` as // `--region-color-*` tokens (shared with `FilteredCharacterList`). +// On the house index the tint answers the hover, so the resting grid +// stays quiet metal and the region flares when a crest is engaged. .cardNorth { &:hover, &:focus-visible { @@ -194,10 +222,10 @@ .name { font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 0.5px; - font-size: 1rem; - font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + font-size: 0.95rem; + font-weight: 400; min-height: 60px; align-content: center; } @@ -212,6 +240,7 @@ .card { display: grid; grid-template-columns: 4.5rem 1fr auto; + justify-items: start; align-items: center; gap: 1rem; min-height: 0; @@ -221,8 +250,8 @@ } .name { - font-size: 1.5rem; - letter-spacing: 1px; + font-size: 1.4rem; + letter-spacing: 0.06em; min-height: 0; } } @@ -230,8 +259,10 @@ .region { font-family: var(--font-ui); font-size: 0.78rem; - font-variant: small-caps; - letter-spacing: 1.5px; - color: var(--ink-faded); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--gaslight-faint); text-align: right; + justify-self: end; } diff --git a/components/FilteredWeaponList/FilteredWeaponList.module.scss b/components/FilteredWeaponList/FilteredWeaponList.module.scss index 7ad16782..b270ad81 100644 --- a/components/FilteredWeaponList/FilteredWeaponList.module.scss +++ b/components/FilteredWeaponList/FilteredWeaponList.module.scss @@ -1,7 +1,7 @@ @use "breakpoints" as bp; // `ParchmentLayout`'s `.page` uses `justify-items: center`, so `justify-self: -// stretch` opts this grid back out to fill the full parchment column. Mirrors +// stretch` opts this grid back out to fill the full page column. Mirrors // the row style on the `/battles` index. .list { display: grid; @@ -23,36 +23,45 @@ gap: 1rem; align-items: center; padding: 0.5rem 0.75rem; - border-radius: 0.25rem; + border-radius: var(--bevel-sm); + corner-shape: bevel; text-decoration: none; - color: var(--ink); + color: var(--gaslight); transition: background-color 0.15s ease; &:hover { - background-color: rgba(212, 162, 89, 0.16); + background-color: rgba(176, 129, 58, 0.08); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; } } .name { - display: inline-flex; + display: inline-grid; + grid-auto-flow: column; align-items: center; + justify-content: start; gap: 0.5rem; font-family: var(--font-ui); font-size: 1.05rem; } +// A charged node marks an entry with a full article behind it. .indicator { - flex: none; width: 0.5rem; height: 0.5rem; border-radius: 50%; - background-color: var(--gold-leaf); + background-color: var(--verdigris); + box-shadow: + inset 0 1px 1px rgba(255, 255, 255, 0.28), + 0 1px 2px rgba(8, 5, 2, 0.5); } // Keep the sigil small so it never grows the row taller than the name's // line box. .sigil { - display: inline-flex; + display: inline-grid; align-items: center; - flex: none; } diff --git a/components/HouseInfobox/HouseInfobox.module.scss b/components/HouseInfobox/HouseInfobox.module.scss index 198ced17..b94d468d 100644 --- a/components/HouseInfobox/HouseInfobox.module.scss +++ b/components/HouseInfobox/HouseInfobox.module.scss @@ -1,18 +1,24 @@ @use "breakpoints" as bp; +// The infobox shell shared by every entity infobox (house, battle, weapon, +// dragon): the aside plate, the sigil stage, and the definition-list stack. +// The per-row `dt`/`dd` treatment lives in `Infobox.module.scss`. .infobox { display: grid; grid-template-columns: 1fr; width: 100%; padding: 0 0 1.25rem; overflow: hidden; - background: rgba(248, 236, 208, 0.6); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 4px; - box-shadow: 0 2px 12px rgba(40, 25, 10, 0.12); + background: var(--plate); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.05), + var(--shadow-soft); font-family: var(--font-ui); - font-size: 0.82rem; - color: var(--ink); + font-size: 0.9rem; + color: var(--gaslight); justify-items: stretch; gap: 0; @@ -40,8 +46,8 @@ } .rows { - display: flex; - flex-direction: column; + display: grid; + grid-template-columns: 1fr; gap: 0.55rem; padding: 1rem 1rem 0; } diff --git a/components/Infobox/Infobox.module.scss b/components/Infobox/Infobox.module.scss index 8262ec95..09ab5eea 100644 --- a/components/Infobox/Infobox.module.scss +++ b/components/Infobox/Infobox.module.scss @@ -1,5 +1,8 @@ @use "breakpoints" as bp; +// The single infobox row: an engraved brass label plate beside its value. +// Used by `InfoRow` and by every entity infobox; the aside shell around +// these rows lives in `HouseInfobox.module.scss`. .row { display: grid; grid-template-columns: 6.5rem 1fr; @@ -8,33 +11,35 @@ dt { font-family: var(--font-sans); - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 1px; - color: var(--ink-faded); + font-size: 0.72rem; font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--brass); } dd { - color: var(--ink); + color: var(--gaslight); line-height: 1.35; font-size: 1rem; + margin: 0; } ul { list-style: none; padding: 0; - display: flex; - flex-direction: column; + margin: 0; + display: grid; + grid-template-columns: 1fr; gap: 0.15rem; } a { - color: var(--ink); - text-decoration-color: rgba(107, 68, 35, 0.35); + color: var(--gaslight); + text-decoration-color: rgba(176, 129, 58, 0.35); &:hover { - color: var(--gold-leaf); + color: var(--verdigris); } } @@ -44,7 +49,7 @@ } .note { - color: var(--ink-faded); + color: var(--gaslight-dim); font-style: italic; font-size: 0.92em; } diff --git a/components/MainMenu/MainMenu.module.scss b/components/MainMenu/MainMenu.module.scss index 203a2248..44db8268 100644 --- a/components/MainMenu/MainMenu.module.scss +++ b/components/MainMenu/MainMenu.module.scss @@ -1,48 +1,15 @@ -@use "breakpoints" as bp; - +// The bound case around the contents ledger. Rows draw their own seams; +// the case supplies the plate, the chamfered corners, and the well. .menu { display: grid; - grid-template-columns: repeat(6, 1fr); - grid-template-areas: - "maps maps timeline timeline houses houses" - "characters characters weapons weapons dragons dragons"; - gap: 1.5rem; - - > :nth-child(1) { - grid-area: maps; - } - > :nth-child(2) { - grid-area: timeline; - } - > :nth-child(3) { - grid-area: houses; - } - > :nth-child(4) { - grid-area: characters; - } - > :nth-child(5) { - grid-area: weapons; - } - > :nth-child(6) { - grid-area: dragons; - } - - @media (max-width: bp.$lg) { - grid-template-columns: repeat(2, 1fr); - grid-template-areas: - "maps timeline" - "houses characters" - "weapons dragons"; - } - - @media (max-width: bp.$md) { - grid-template-columns: 1fr; - grid-template-areas: - "maps" - "timeline" - "houses" - "characters" - "weapons" - "dragons"; - } + align-content: start; + width: 100%; + background: var(--well); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel); + corner-shape: bevel; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.04), + var(--shadow-soft); + overflow: hidden; } diff --git a/components/MainMenu/MainMenu.test.tsx b/components/MainMenu/MainMenu.test.tsx index 9d905ff8..a98d09e6 100644 --- a/components/MainMenu/MainMenu.test.tsx +++ b/components/MainMenu/MainMenu.test.tsx @@ -3,7 +3,7 @@ import { render, screen } from "@testing-library/react"; import { MainMenu } from "@/components/MainMenu"; describe("MainMenu", () => { - it("renders only visible tiles in order: Maps, Timeline, Houses, Characters, Weapons, Battles", () => { + it("renders only visible entries in plate order: Maps, Houses, Characters, Timeline, Battles, Weapons", () => { render(); const links = screen.getAllByRole("link"); expect(links).toHaveLength(6); @@ -11,23 +11,33 @@ describe("MainMenu", () => { expect(links[0].textContent).toContain("Maps"); expect(links[0].getAttribute("href")).toBe("/maps/"); - expect(links[1].textContent).toContain("Timeline"); - expect(links[1].getAttribute("href")).toBe("/timeline/"); + expect(links[1].textContent).toContain("Houses"); + expect(links[1].getAttribute("href")).toBe("/houses/"); - expect(links[2].textContent).toContain("Houses"); - expect(links[2].getAttribute("href")).toBe("/houses/"); + expect(links[2].textContent).toContain("Characters"); + expect(links[2].getAttribute("href")).toBe("/characters/"); - expect(links[3].textContent).toContain("Characters"); - expect(links[3].getAttribute("href")).toBe("/characters/"); + expect(links[3].textContent).toContain("Timeline"); + expect(links[3].getAttribute("href")).toBe("/timeline/"); - expect(links[4].textContent).toContain("Weapons"); - expect(links[4].getAttribute("href")).toBe("/weapons/"); + expect(links[4].textContent).toContain("Battles"); + expect(links[4].getAttribute("href")).toBe("/battles/"); - expect(links[5].textContent).toContain("Battles"); - expect(links[5].getAttribute("href")).toBe("/battles/"); + expect(links[5].textContent).toContain("Weapons"); + expect(links[5].getAttribute("href")).toBe("/weapons/"); }); - it("does not render hidden tiles (Dragons)", () => { + it("numbers the visible entries as plates I through VI", () => { + render(); + const links = screen.getAllByRole("link"); + const numerals = ["I", "II", "III", "IV", "V", "VI"]; + links.reduce((expected, link) => { + expect(link.textContent).toContain(`Plate${expected[0]}`); + return expected.slice(1); + }, numerals); + }); + + it("does not render hidden entries (Dragons)", () => { render(); const hrefs = screen .getAllByRole("link") @@ -35,7 +45,7 @@ describe("MainMenu", () => { expect(hrefs).not.toContain("/dragons/"); }); - it('wraps tiles in a nav landmark labelled "Atlas sections"', () => { + it('wraps entries in a nav landmark labelled "Atlas sections"', () => { render(); expect( screen.getByRole("navigation", { name: /atlas sections/i }), diff --git a/components/MainMenu/MainMenu.tsx b/components/MainMenu/MainMenu.tsx index 5007a1be..6f1ce735 100644 --- a/components/MainMenu/MainMenu.tsx +++ b/components/MainMenu/MainMenu.tsx @@ -2,46 +2,55 @@ import { MainMenuTile } from "@/components/MainMenuTile"; import { sectionGlyphs } from "@/components/SectionGlyphs/SectionGlyphs"; import styles from "@/components/MainMenu/MainMenu.module.scss"; +// The contents ledger: every section of the atlas bound in one case, each +// entry numbered as an engraved plate. export function MainMenu() { return (
  • ); diff --git a/components/SiteMenu/SiteMenu.module.scss b/components/SiteMenu/SiteMenu.module.scss index 20b4a357..17c0631f 100644 --- a/components/SiteMenu/SiteMenu.module.scss +++ b/components/SiteMenu/SiteMenu.module.scss @@ -10,25 +10,28 @@ width: 2.5rem; height: 2.5rem; padding: 0; - background: transparent; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - color: var(--ink); + background: rgba(29, 23, 18, 0.7); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + color: var(--gaslight-dim); cursor: pointer; transition: color 150ms ease, border-color 150ms ease, - background 150ms ease; + background 150ms ease, + box-shadow 150ms ease; &:hover, &:focus-visible { - color: var(--gold-leaf); - border-color: var(--gold-leaf); - background: rgba(248, 236, 208, 0.6); + color: var(--verdigris); + border-color: var(--edge-brass); + background: rgba(39, 31, 22, 0.9); + box-shadow: var(--focus-glow); } &:focus-visible { - outline: 2px solid var(--gold-leaf); + outline: 2px solid var(--verdigris); outline-offset: 2px; } @@ -45,7 +48,7 @@ .backdrop { position: fixed; inset: 0; - background: rgba(45, 30, 15, 0.45); + background: rgba(8, 5, 2, 0.6); opacity: 0; pointer-events: none; transition: opacity 220ms ease; @@ -67,13 +70,9 @@ bottom: 0; left: 0; width: min(20rem, 86vw); - background: linear-gradient( - 180deg, - var(--vellum) 0%, - var(--parchment-light) 100% - ); - border-right: 1px solid rgba(107, 68, 35, 0.35); - box-shadow: 8px 0 24px rgba(45, 30, 15, 0.18); + background: linear-gradient(180deg, #2b2217 0%, #1b1510 100%); + border-right: 1px solid rgba(176, 129, 58, 0.3); + box-shadow: var(--shadow-raised); transform: translateX(-100%); transition: transform 240ms ease; z-index: 100; @@ -91,8 +90,8 @@ background: linear-gradient( 180deg, transparent 0%, - var(--gold-leaf) 18%, - var(--gold-leaf) 82%, + var(--brass) 18%, + var(--brass) 82%, transparent 100% ); opacity: 0.55; @@ -117,16 +116,17 @@ grid-template-columns: 1fr auto; align-items: center; padding: 1rem 1.1rem; - border-bottom: 1px solid rgba(107, 68, 35, 0.25); + border-bottom: 1px solid var(--edge); gap: 0.5rem; } .panelLabel { - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 4px; - font-size: 0.95rem; - color: var(--ink-faded); + font-family: var(--font-ui); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.3em; + font-size: 0.8rem; + color: var(--brass); } .close { @@ -137,8 +137,9 @@ padding: 0; background: transparent; border: 1px solid transparent; - border-radius: 2px; - color: var(--ink-faded); + border-radius: var(--bevel-sm); + corner-shape: bevel; + color: var(--gaslight-faint); cursor: pointer; transition: color 150ms ease, @@ -146,12 +147,12 @@ &:hover, &:focus-visible { - color: var(--gold-leaf); - border-color: var(--gold-leaf); + color: var(--verdigris); + border-color: var(--edge-brass); } &:focus-visible { - outline: 2px solid var(--gold-leaf); + outline: 2px solid var(--verdigris); outline-offset: 2px; } @@ -180,13 +181,13 @@ grid-template-columns: auto 1fr; align-items: center; gap: 0.85rem; - padding: 0.7rem 1.5rem; - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 3px; - font-size: 1.05rem; + padding: 0.75rem 1.5rem; + font-family: var(--font-ui); font-weight: 500; - color: var(--ink-faded); + text-transform: uppercase; + letter-spacing: 0.16em; + font-size: 1rem; + color: var(--gaslight-dim); text-decoration: none; border-left: 3px solid transparent; transition: @@ -196,16 +197,16 @@ &:hover, &:focus-visible { - color: var(--ink); - background: rgba(212, 162, 89, 0.12); - border-left-color: var(--gold-leaf); + color: var(--gaslight); + background: rgba(176, 129, 58, 0.08); + border-left-color: var(--brass); } @media (max-width: bp.$xs) { - padding: 0.7rem 1.25rem; + padding: 0.75rem 1.25rem; gap: 0.75rem; - font-size: 1rem; - letter-spacing: 2.5px; + font-size: 0.95rem; + letter-spacing: 0.14em; } @media (prefers-reduced-motion: reduce) { @@ -219,29 +220,19 @@ width: 1.25rem; height: 1.25rem; overflow: hidden; - border-radius: 2px; - transition: border-color 150ms ease; - - img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; - } + color: var(--brass); + transition: + color 150ms ease, + filter 150ms ease; - // Section glyphs render as inline SVG rather than a raster image; scale them - // to the icon box so they sit centred alongside the PNG icons. + // Section glyphs render as inline SVG in currentColor; scale them to the + // icon box so every row carries the same weight of line. svg { width: 100%; height: 100%; display: block; } - .link:hover &, - .link:focus-visible & { - border-color: var(--gold-leaf); - } - @media (prefers-reduced-motion: reduce) { transition: none; } @@ -251,12 +242,13 @@ display: block; } +// The open section wears verdigris: the oxidised rail marks where you are. .linkActive { - color: var(--ink); - border-left-color: var(--gold-leaf); - background: rgba(212, 162, 89, 0.18); + color: var(--gaslight); + border-left-color: var(--verdigris); + background: rgba(121, 164, 140, 0.07); .linkIcon { - border-color: var(--gold-leaf); + color: var(--verdigris); } } diff --git a/components/SiteMenu/SiteMenu.test.tsx b/components/SiteMenu/SiteMenu.test.tsx index 61460884..82f3a745 100644 --- a/components/SiteMenu/SiteMenu.test.tsx +++ b/components/SiteMenu/SiteMenu.test.tsx @@ -19,7 +19,7 @@ describe("SiteMenu", () => { expect(links).toHaveLength(0); }); - it("reveals only the visible primary nav items (Maps, Timeline, Houses, Characters, Weapons, Battles) when opened", () => { + it("reveals only the visible primary nav items in plate order (Maps, Houses, Characters, Timeline, Battles, Weapons) when opened", () => { render(); fireEvent.click(screen.getByRole("button", { name: /open menu/i })); @@ -30,19 +30,19 @@ describe("SiteMenu", () => { const hrefs = links.map((l) => l.getAttribute("href") ?? ""); expect(hrefs).toEqual([ "/maps/", - "/timeline/", "/houses/", "/characters/", - "/weapons/", + "/timeline/", "/battles/", + "/weapons/", ]); expect(links.map((l) => l.textContent?.trim())).toEqual([ "Maps", - "Timeline", "Houses", "Characters", - "Weapons", + "Timeline", "Battles", + "Weapons", ]); }); diff --git a/components/SiteMenu/SiteMenu.tsx b/components/SiteMenu/SiteMenu.tsx index 410da603..ed01b6e6 100644 --- a/components/SiteMenu/SiteMenu.tsx +++ b/components/SiteMenu/SiteMenu.tsx @@ -1,6 +1,5 @@ "use client"; -import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { type ReactNode, useEffect, useId, useRef, useState } from "react"; @@ -12,53 +11,53 @@ import styles from "@/components/SiteMenu/SiteMenu.module.scss"; type MenuItem = { href: string; label: string; - icon: string; - glyph?: ReactNode; + glyph: ReactNode; visible: boolean; }; +// Every entry uses its section's line-art glyph so the drawer stays +// monochrome metal, listed in the same plate order as the homepage ledger. const ITEMS: MenuItem[] = [ { href: "/maps/", label: "Maps", - icon: "/menu-icons/map.png", - visible: true, - }, - { - href: "/timeline/", - label: "Timeline", - icon: "/menu-icons/timeline.png", + glyph: sectionGlyphs.maps, visible: true, }, { href: "/houses/", label: "Houses", - icon: "/menu-icons/houses.png", + glyph: sectionGlyphs.houses, visible: true, }, { href: "/characters/", label: "Characters", - icon: "/menu-icons/characters.png", + glyph: sectionGlyphs.characters, visible: true, }, { - href: "/weapons/", - label: "Weapons", - icon: "/menu-icons/weapons.png", + href: "/timeline/", + label: "Timeline", + glyph: sectionGlyphs.timeline, visible: true, }, { href: "/battles/", label: "Battles", - icon: "/menu-icons/battles.png", glyph: sectionGlyphs.battles, visible: true, }, + { + href: "/weapons/", + label: "Weapons", + glyph: sectionGlyphs.weapons, + visible: true, + }, { href: "/dragons/", label: "Dragons", - icon: "/menu-icons/dragons.png", + glyph: sectionGlyphs.dragons, visible: false, }, ]; @@ -190,15 +189,7 @@ export function SiteMenu() { tabIndex={isOpen ? 0 : -1} > {item.label} diff --git a/components/SortToggle/SortToggle.module.scss b/components/SortToggle/SortToggle.module.scss index 44843d1e..0b680d3f 100644 --- a/components/SortToggle/SortToggle.module.scss +++ b/components/SortToggle/SortToggle.module.scss @@ -1,11 +1,13 @@ @use "breakpoints" as bp; .toggle { - display: flex; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + display: grid; + grid-auto-flow: column; + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; overflow: hidden; - background: rgba(248, 236, 208, 0.55); + background: var(--well); width: 100px; height: 50px; @@ -19,31 +21,33 @@ background: transparent; border: 0; padding: 0; - display: flex; - align-items: center; + display: grid; + place-items: center; width: 50px; height: 50px; - justify-content: center; - color: var(--ink-faded); + color: var(--gaslight-faint); cursor: pointer; transition: background 120ms ease, - color 120ms ease; + color 120ms ease, + box-shadow 120ms ease; & + & { - border-left: 1px solid rgba(107, 68, 35, 0.35); + border-left: 1px solid var(--edge); } &:hover, &:focus-visible { - color: var(--gold-leaf); + color: var(--brass-bright); outline: none; - background: rgba(248, 236, 208, 0.9); + background: rgba(176, 129, 58, 0.08); } + // The engaged switch runs charged. &[aria-pressed="true"] { - color: var(--ink); - background: rgba(212, 162, 89, 0.18); + color: var(--verdigris); + background: rgba(121, 164, 140, 0.1); + box-shadow: inset 0 0 10px rgba(121, 164, 140, 0.12); } @media (prefers-reduced-motion: reduce) { diff --git a/components/Sources/Sources.module.scss b/components/Sources/Sources.module.scss index 53dacd5b..0e62a6b2 100644 --- a/components/Sources/Sources.module.scss +++ b/components/Sources/Sources.module.scss @@ -1,8 +1,8 @@ .sources { padding: 1.5rem; - border-top: 1px solid rgba(107, 68, 35, 0.3); + border-top: 1px solid var(--edge); font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); + color: var(--gaslight-faint); width: 100%; } diff --git a/components/TimelineChart/TimelineChart.module.scss b/components/TimelineChart/TimelineChart.module.scss index 0b8de9a5..fc847647 100644 --- a/components/TimelineChart/TimelineChart.module.scss +++ b/components/TimelineChart/TimelineChart.module.scss @@ -23,7 +23,7 @@ .header { padding-block: 0.5rem 0.75rem; - border-bottom: 1px solid rgba(107, 68, 35, 0.25); + border-bottom: 1px solid var(--edge); } .axisSpacer { @@ -33,12 +33,12 @@ .columnHeading { margin: 0; font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 2px; - font-size: 1.15rem; - font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 1.05rem; + font-weight: 400; text-align: center; - color: var(--ink); + color: var(--gaslight); &:nth-of-type(1) { grid-area: westeros; @@ -64,12 +64,12 @@ .axisRail { grid-area: axis; - border-right: 1px solid rgba(107, 68, 35, 0.25); + border-right: 1px solid var(--edge-strong); } %column { position: relative; - border-right: 1px dashed rgba(107, 68, 35, 0.12); + border-right: 1px dashed rgba(190, 158, 110, 0.12); &:last-of-type { border-right: none; @@ -102,21 +102,24 @@ display: grid; grid-template-areas: "label"; justify-items: start; - border-top: 1px dashed rgba(107, 68, 35, 0.18); + border-top: 1px dashed rgba(190, 158, 110, 0.16); } +// Year labels along the axis: instrument figures on the dial. .tickLabel { grid-area: label; width: calc(var(--axis-width) - var(--era-width)); padding-right: 1.25rem; text-align: right; transform: translateY(-50%); - background: var(--parchment-light); - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 2px; - font-size: 1.15rem; - color: var(--ink); + background: var(--iron); + font-family: var(--font-ui); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.95rem; + color: var(--gaslight-dim); + font-variant-numeric: tabular-nums; } .era { @@ -129,20 +132,22 @@ grid-template-columns: var(--era-width) 1fr; align-items: center; justify-items: center; - background: rgba(212, 162, 89, 0.07); - border-top: 1px solid rgba(212, 162, 89, 0.22); - border-bottom: 1px solid rgba(212, 162, 89, 0.22); + background: rgba(176, 129, 58, 0.05); + border-top: 1px solid rgba(176, 129, 58, 0.2); + border-bottom: 1px solid rgba(176, 129, 58, 0.2); } .eraLabel { grid-area: label; writing-mode: vertical-rl; transform: rotate(180deg); - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 4px; - font-size: 1rem; - color: var(--ink-faded); + font-family: var(--font-ui); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.32em; + font-size: 0.85rem; + color: var(--brass); + opacity: 0.85; } .node { @@ -168,20 +173,21 @@ column-gap: 0.5rem; row-gap: 0.1rem; padding: 0.3rem 0.55rem; - background: var(--vellum); - border: 1px solid rgba(107, 68, 35, 0.25); - border-radius: 2px; - color: var(--ink); + background: var(--plate); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + color: var(--gaslight); text-decoration: none; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04); + box-shadow: var(--shadow-soft); transition: border-color 150ms ease, box-shadow 150ms ease; &:hover, &:focus-visible { - border-color: var(--gold-leaf); - box-shadow: 0 2px 12px rgba(212, 162, 89, 0.18); + border-color: var(--edge-brass); + box-shadow: var(--shadow-soft), var(--brass-glow); } @media (prefers-reduced-motion: reduce) { @@ -189,14 +195,18 @@ } } +// Every event is a charged node on the conduit of years. .marker { grid-area: marker; align-self: center; width: 0.5rem; height: 0.5rem; - background: var(--gold-leaf); - border: 1px solid rgba(107, 68, 35, 0.4); + background: var(--verdigris); + border: 1px solid rgba(67, 95, 78, 0.9); border-radius: 50%; + box-shadow: + inset 0 1px 1px rgba(255, 255, 255, 0.28), + 0 1px 2px rgba(8, 5, 2, 0.5); } .eventName { @@ -209,7 +219,7 @@ grid-area: when; font-style: italic; font-size: 0.8rem; - color: var(--ink-faded); + color: var(--gaslight-dim); } @media (max-width: bp.$md) { @@ -219,13 +229,13 @@ } .columnHeading { - font-size: 1rem; - letter-spacing: 1px; + font-size: 0.95rem; + letter-spacing: 0.06em; } .tickLabel { padding-right: 0.75rem; - letter-spacing: 1px; - font-size: 0.9rem; + letter-spacing: 0.06em; + font-size: 0.85rem; } } diff --git a/components/TimelineCluster/TimelineCluster.module.scss b/components/TimelineCluster/TimelineCluster.module.scss index 714b9054..6fa1fcf4 100644 --- a/components/TimelineCluster/TimelineCluster.module.scss +++ b/components/TimelineCluster/TimelineCluster.module.scss @@ -10,24 +10,25 @@ align-items: baseline; gap: 0.5rem; padding: 0.3rem 0.75rem; - background: var(--vellum); - border: 1px solid rgba(107, 68, 35, 0.35); + background: var(--plate); + border: 1px solid var(--edge-strong); border-radius: 999px; - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 1px; + font-family: var(--font-ui); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; font-size: 0.85rem; - color: var(--wax-seal); + color: var(--brass-bright); cursor: pointer; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04); + box-shadow: var(--shadow-soft); transition: border-color 150ms ease, box-shadow 150ms ease; &:hover, &:focus-visible { - border-color: var(--gold-leaf); - box-shadow: 0 2px 12px rgba(212, 162, 89, 0.18); + border-color: var(--edge-brass); + box-shadow: var(--shadow-soft), var(--brass-glow); } @media (prefers-reduced-motion: reduce) { @@ -42,10 +43,10 @@ .when { font-family: var(--font-body); font-style: italic; - font-variant: normal; + text-transform: none; letter-spacing: 0; font-size: 0.8rem; - color: var(--ink-faded); + color: var(--gaslight-dim); } .list { @@ -61,10 +62,11 @@ padding: 0.35rem; margin: 0; list-style: none; - background: var(--vellum); - border: 1px solid var(--gold-leaf); - border-radius: 2px; - box-shadow: 0 4px 18px rgba(61, 40, 23, 0.25); + background: #2b2217; + border: 1px solid var(--edge-brass); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: var(--shadow-raised); } .item { @@ -78,14 +80,14 @@ align-items: baseline; gap: 0.75rem; padding: 0.35rem 0.5rem; - color: var(--ink); + color: var(--gaslight); text-decoration: none; border-radius: 2px; &:hover, &:focus-visible { - background: rgba(212, 162, 89, 0.16); - color: var(--ink); + background: rgba(176, 129, 58, 0.1); + color: var(--gaslight); } } @@ -100,6 +102,6 @@ font-family: var(--font-body); font-style: italic; font-size: 0.8rem; - color: var(--ink-faded); + color: var(--gaslight-dim); white-space: nowrap; } diff --git a/components/TimelineExplorer/TimelineExplorer.module.scss b/components/TimelineExplorer/TimelineExplorer.module.scss index e74c134d..ea3268f0 100644 --- a/components/TimelineExplorer/TimelineExplorer.module.scss +++ b/components/TimelineExplorer/TimelineExplorer.module.scss @@ -8,7 +8,7 @@ display: grid; } -// Floating pill in the lower-right, clear of the sticky site header and the +// Floating pod in the lower-right, clear of the sticky site header and the // vertically-centred minimap. Fixed so it stays reachable down the long chart. .zoom { position: fixed; @@ -19,12 +19,13 @@ justify-items: center; gap: 0.25rem; padding: 0.35rem; - background: rgba(248, 236, 208, 0.94); - border: 1px solid rgba(107, 68, 35, 0.3); - border-radius: 2px; - box-shadow: 0 2px 12px rgba(61, 40, 23, 0.15); - backdrop-filter: blur(3px); - -webkit-backdrop-filter: blur(3px); + background: rgba(27, 21, 16, 0.92); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: var(--shadow-raised); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); } .zoomButton { @@ -34,12 +35,13 @@ width: 2.25rem; height: 2.25rem; padding: 0; - background: rgba(248, 236, 208, 0.6); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - color: var(--ink); - font-family: var(--font-heading); - font-size: 1.4rem; + background: var(--plate); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + color: var(--gaslight-dim); + font-family: var(--font-ui); + font-size: 1.35rem; line-height: 1; cursor: pointer; transition: @@ -49,9 +51,8 @@ &:hover:not(:disabled), &:focus-visible:not(:disabled) { - border-color: var(--gold-leaf); - color: var(--gold-leaf); - background: rgba(248, 236, 208, 0.9); + border-color: var(--edge-brass); + color: var(--brass-bright); outline: none; } @@ -67,10 +68,12 @@ .zoomLevel { min-width: 2.25rem; - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 1px; + font-family: var(--font-ui); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; font-size: 0.85rem; - color: var(--ink-faded); + color: var(--gaslight-faint); text-align: center; + font-variant-numeric: tabular-nums; } diff --git a/components/TimelineMinimap/TimelineMinimap.module.scss b/components/TimelineMinimap/TimelineMinimap.module.scss index 31e78bd4..1ae70033 100644 --- a/components/TimelineMinimap/TimelineMinimap.module.scss +++ b/components/TimelineMinimap/TimelineMinimap.module.scss @@ -9,10 +9,11 @@ max-height: 82vh; overflow-y: auto; padding: 0.5rem 0.25rem; - background: rgba(248, 236, 208, 0.92); - border: 1px solid rgba(107, 68, 35, 0.3); - border-radius: 2px; - box-shadow: 0 2px 12px rgba(61, 40, 23, 0.15); + background: rgba(27, 21, 16, 0.92); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: var(--shadow-soft); @media (max-width: bp.$lg) { display: none; @@ -37,16 +38,25 @@ border: none; text-align: right; cursor: pointer; - font-family: var(--font-heading); - font-variant: small-caps; - letter-spacing: 1px; + font-family: var(--font-ui); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.08em; font-size: 0.75rem; - color: var(--ink-faded); + color: var(--gaslight-faint); border-radius: 2px; + font-variant-numeric: tabular-nums; + transition: + color 120ms ease, + background 120ms ease; &:hover, &:focus-visible { - color: var(--wax-seal); - background: rgba(212, 162, 89, 0.16); + color: var(--verdigris); + background: rgba(121, 164, 140, 0.08); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; } } diff --git a/components/ViewToggle/ViewToggle.module.scss b/components/ViewToggle/ViewToggle.module.scss index 9e2f9881..74d7abc0 100644 --- a/components/ViewToggle/ViewToggle.module.scss +++ b/components/ViewToggle/ViewToggle.module.scss @@ -1,11 +1,13 @@ @use "breakpoints" as bp; .toggle { - display: flex; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + display: grid; + grid-auto-flow: column; + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; overflow: hidden; - background: rgba(248, 236, 208, 0.55); + background: var(--well); height: 50px; @media (max-width: bp.$md) { @@ -18,31 +20,33 @@ background: transparent; border: 0; padding: 0; - display: flex; - align-items: center; + display: grid; + place-items: center; width: 50px; height: 50px; - justify-content: center; - color: var(--ink-faded); + color: var(--gaslight-faint); cursor: pointer; transition: background 120ms ease, - color 120ms ease; + color 120ms ease, + box-shadow 120ms ease; & + & { - border-left: 1px solid rgba(107, 68, 35, 0.35); + border-left: 1px solid var(--edge); } &:hover, &:focus-visible { - color: var(--gold-leaf); + color: var(--brass-bright); outline: none; - background: rgba(248, 236, 208, 0.9); + background: rgba(176, 129, 58, 0.08); } + // The engaged view runs charged. &[aria-pressed="true"] { - color: var(--ink); - background: rgba(212, 162, 89, 0.18); + color: var(--verdigris); + background: rgba(121, 164, 140, 0.1); + box-shadow: inset 0 0 10px rgba(121, 164, 140, 0.12); } @media (prefers-reduced-motion: reduce) { diff --git a/components/WorldMap/WorldMap.module.scss b/components/WorldMap/WorldMap.module.scss index e5384a0b..3b3c9368 100644 --- a/components/WorldMap/WorldMap.module.scss +++ b/components/WorldMap/WorldMap.module.scss @@ -8,6 +8,8 @@ justify-items: center; } +// The chart table: the parchment map is an exhibit under glass, held in a +// brass instrument bezel; the chrome around it stays oiled iron. .stage { position: relative; width: 100%; @@ -19,24 +21,28 @@ height: auto; aspect-ratio: 1 / 1; } - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - background: rgba(248, 236, 208, 0.55); + border: 1px solid var(--edge-brass); + border-radius: var(--bevel); + corner-shape: bevel; + background: var(--well); + box-shadow: + inset 0 0 40px rgba(8, 5, 2, 0.5), + var(--shadow-soft); overflow: hidden; display: grid; grid-template-areas: "canvas"; &:focus-visible { - outline: 2px solid var(--gold-leaf); + outline: 2px solid var(--verdigris); outline-offset: 2px; } &::before { - content: "Unfurling the map…"; + content: "Focusing the lens…"; grid-area: canvas; place-self: center; font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); } &:fullscreen { @@ -56,8 +62,8 @@ cursor: pointer; circle { - fill: rgba(212, 162, 89, 0); - stroke: rgba(212, 162, 89, 0); + fill: rgba(121, 164, 140, 0); + stroke: rgba(121, 164, 140, 0); stroke-width: 3; transition: fill 120ms ease, @@ -66,8 +72,8 @@ &:hover circle, &:focus-visible circle { - fill: rgba(212, 162, 89, 0.18); - stroke: var(--gold-leaf); + fill: rgba(121, 164, 140, 0.15); + stroke: var(--verdigris); } &:focus-visible { @@ -89,10 +95,11 @@ grid-template-columns: auto auto; gap: 0.15rem 0.5rem; padding: 0.5rem 0.75rem; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - background: rgba(248, 236, 208, 0.85); - color: var(--ink-faded); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + background: rgba(27, 21, 16, 0.9); + color: var(--gaslight-dim); font-size: 0.85rem; font-variant-numeric: tabular-nums; pointer-events: none; @@ -157,19 +164,22 @@ width: 44px; height: 44px; padding: 0; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - background: rgba(248, 236, 208, 0.85); - color: var(--ink-faded); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + background: rgba(27, 21, 16, 0.88); + color: var(--gaslight-dim); cursor: pointer; transition: background 120ms ease, - color 120ms ease; + color 120ms ease, + border-color 120ms ease; &:hover, &:focus-visible { - color: var(--gold-leaf); - background: rgba(248, 236, 208, 0.95); + color: var(--verdigris); + border-color: rgba(121, 164, 140, 0.5); + background: rgba(27, 21, 16, 0.96); outline: none; } @@ -179,8 +189,8 @@ } .hint { - font-size: 1rem; + font-size: 0.95rem; font-style: italic; text-align: center; - color: var(--ink-faded); + color: var(--gaslight-faint); } diff --git a/components/listSearch.module.scss b/components/listSearch.module.scss index e547a446..466f5171 100644 --- a/components/listSearch.module.scss +++ b/components/listSearch.module.scss @@ -7,11 +7,12 @@ font-family: var(--font-character-name); font-size: 1.8rem; padding: 1rem; - background: rgba(248, 236, 208, 0.55); - color: var(--ink); - // height: 50px; - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: var(--well); + color: var(--gaslight); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.35); outline: none; transition: border-color 150ms ease, @@ -19,15 +20,16 @@ box-shadow 150ms ease; &::placeholder { - color: var(--ink-faded); - font-style: italic; + color: var(--gaslight-faint); } &:focus, &:focus-visible { - border-color: var(--gold-leaf); - background: rgba(248, 236, 208, 0.9); - box-shadow: 0 0 0 2px rgba(212, 162, 89, 0.18); + border-color: rgba(121, 164, 140, 0.6); + background: rgba(15, 10, 5, 0.8); + box-shadow: + inset 0 2px 6px rgba(0, 0, 0, 0.35), + var(--focus-glow); } @media (prefers-reduced-motion: reduce) { @@ -42,7 +44,7 @@ .empty { font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); text-align: center; padding: 2.5rem 1rem; } @@ -50,23 +52,25 @@ .count { margin: 0; font-family: var(--font-ui); - font-size: 0.85rem; - color: var(--ink-faded); - letter-spacing: 1.5px; - font-variant: small-caps; + font-size: 0.8rem; + font-weight: 500; + color: var(--gaslight-faint); + letter-spacing: 0.16em; + text-transform: uppercase; } .pagination { - display: flex; + display: grid; + grid-auto-flow: column; align-items: center; justify-content: center; gap: 1rem; font-family: var(--font-ui); font-size: 0.9rem; - color: var(--ink-faded); + color: var(--gaslight-dim); @media (max-width: bp.$sm) { - display: grid; + grid-auto-flow: row; grid-template-columns: auto 1fr auto; grid-template-areas: "prev status next" @@ -98,7 +102,7 @@ } } -// `.paginationTop` / `.paginationBottom` are marker classes — they carry no +// `.paginationTop` / `.paginationBottom` are marker classes. They carry no // styles, but their presence on the element lets tests identify the top // vs bottom pagination nav by `classList.contains(...)`. .paginationTop { @@ -108,14 +112,17 @@ .button { appearance: none; - background: rgba(248, 236, 208, 0.6); - color: var(--ink); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; - padding: 0.4rem 0.85rem; + background: var(--plate); + color: var(--gaslight-dim); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; + padding: 0.45rem 0.9rem; font-family: var(--font-ui); + font-weight: 500; font-size: 0.85rem; - letter-spacing: 0.5px; + letter-spacing: 0.08em; + text-transform: uppercase; cursor: pointer; transition: border-color 120ms ease, @@ -124,8 +131,8 @@ &:hover:not(:disabled), &:focus-visible:not(:disabled) { - border-color: var(--gold-leaf); - color: var(--gold-leaf); + border-color: var(--edge-brass); + color: var(--brass-bright); outline: none; } @@ -140,35 +147,37 @@ } .status { - font-variant: small-caps; - letter-spacing: 1.5px; + text-transform: uppercase; + letter-spacing: 0.12em; } .pageSize { - display: flex; + display: grid; + grid-auto-flow: column; align-items: center; gap: 0.4rem; font-family: var(--font-ui); font-size: 0.85rem; - color: var(--ink-faded); - letter-spacing: 0.5px; + color: var(--gaslight-faint); + letter-spacing: 0.06em; } .pageSizeSelect { appearance: none; -webkit-appearance: none; - background: rgba(248, 236, 208, 0.6); - color: var(--ink); - border: 1px solid rgba(107, 68, 35, 0.35); - border-radius: 2px; + background: var(--well); + color: var(--gaslight); + border: 1px solid var(--edge-strong); + border-radius: var(--bevel-sm); + corner-shape: bevel; padding: 0.3rem 1.6rem 0.3rem 0.55rem; font-family: var(--font-ui); font-size: 0.85rem; - letter-spacing: 0.5px; + letter-spacing: 0.06em; cursor: pointer; background-image: - linear-gradient(45deg, transparent 50%, rgba(107, 68, 35, 0.55) 50%), - linear-gradient(135deg, rgba(107, 68, 35, 0.55) 50%, transparent 50%); + linear-gradient(45deg, transparent 50%, rgba(176, 129, 58, 0.75) 50%), + linear-gradient(135deg, rgba(176, 129, 58, 0.75) 50%, transparent 50%); background-position: calc(100% - 0.85rem) 55%, calc(100% - 0.55rem) 55%; @@ -183,8 +192,8 @@ &:hover, &:focus-visible { - border-color: var(--gold-leaf); - color: var(--gold-leaf); + border-color: var(--edge-brass); + color: var(--brass-bright); outline: none; } diff --git a/styles/globals.scss b/styles/globals.scss index d7ea618a..5d173427 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -1,23 +1,72 @@ @use "breakpoints" as bp; :root { - --parchment-light: #f4e4c1; - --parchment-dark: #e8d3a0; - --vellum: #f8ecd0; - --ink: #3d2817; - --ink-faded: #6b4423; - --ink-slate: #2e3a55; - --ink-rose: #6a3142; - --wax-seal: #8b1a1a; - --gold-leaf: #d4a259; - --rose-madder: #a85a6b; - - --font-heading: var(--font-cinzel), Georgia, serif; - --font-character-name: var(--font-cormorant-unicase), Georgia, serif; - --font-body: var(--font-eb-garamond), Georgia, serif; - --font-ui: var(--font-eb-garamond), Georgia, serif; + // Tell the browser this is a dark interface so native widgets (date + // pickers, scrollbars, form indicators) render in their dark forms. + color-scheme: dark; + + // Structural darks: soot and oiled iron, warmed by lamplight rather + // than cooled by void. The whole atlas sits on these. + --coal: #14100a; + --iron: #1d1712; + --gunmetal: #271f16; + --gunmetal-raised: #322818; + --steel: #4a3b2b; + + // Metals. Brass is the primary accent, the resting identity of the atlas. + --brass: #b0813a; + --brass-bright: #e4bf78; + --brass-dark: #77571f; + + // Verdigris, oxidised copper. Reserved for interaction: focus, hover, + // live state. Matte, never luminous. + --verdigris: #79a48c; + --verdigris-dim: #435f4e; + + // Raw copper, the rare warm accent. Kept for the exceptional, never routine. + --copper: #c1704a; + + // Gaslight text ramp, warm lamplight on dark metal. + --gaslight: #f0e7d2; + --gaslight-dim: #bfb096; + --gaslight-faint: #8f8069; + + // Signal red for errors and invalid states. + --ember: #d05a3e; + + // Genealogy tints, used by the family tree to mark lines of descent. + --tint-male: #9db6c8; + --tint-female: #d4a3b0; + + // Hairlines and composed surfaces. + --edge: rgba(190, 158, 110, 0.2); + --edge-strong: rgba(190, 158, 110, 0.34); + --edge-brass: rgba(176, 129, 58, 0.5); + --well: rgba(15, 10, 5, 0.55); + --plate: linear-gradient( + 180deg, + var(--gunmetal-raised) 0%, + var(--gunmetal) 100% + ); + + // Shadows and sheens. Depth comes from soot shadow and a faint warm + // lamplight catch on brass; nothing glows of its own accord. + --shadow-soft: 0 2px 10px rgba(8, 5, 2, 0.4); + --shadow-raised: 0 10px 28px rgba(8, 5, 2, 0.5); + --focus-glow: 0 0 0 2px rgba(121, 164, 140, 0.4); + --brass-glow: 0 2px 12px rgba(176, 129, 58, 0.16); + + // Chamfered plate corners; browsers without `corner-shape` fall back to + // plain rounding at the same radius. + --bevel: 10px; + --bevel-sm: 5px; + + --font-heading: var(--font-bevan), "Rockwell", Georgia, serif; + --font-character-name: var(--font-graduate), "Copperplate", serif; + --font-body: var(--font-old-standard), Georgia, serif; + --font-ui: var(--font-oswald), "Arial Narrow", sans-serif; --font-sans: - var(--font-alegreya-sans), -apple-system, BlinkMacSystemFont, sans-serif; + var(--font-oswald), -apple-system, BlinkMacSystemFont, sans-serif; --bp-xs: 480px; --bp-sm: 640px; @@ -25,22 +74,23 @@ --bp-lg: 1024px; --bp-xl: 1280px; - // Regional palette — heraldic border tints used by both the house and - // character list cards. Kept as design tokens so the two list modules + // Regional palette, heraldic border tints used by both the house and + // character list cards, desaturated to sit like hand-tinted lithograph + // inks on the soot ground. Kept as design tokens so the two list modules // don't each carry the same nine hex values. Add a new region here, not // in the module files. - --region-color-north: #5d6f7f; - --region-color-vale: #4d7caa; - --region-color-riverlands: #3d5a7c; - --region-color-westerlands: #b13a3a; - --region-color-reach: #6f8336; - --region-color-stormlands: #c89a31; - --region-color-dorne: #c7693a; - --region-color-iron-islands: #2f4a44; - --region-color-crownlands: #5a2a30; -} - -// Desktop-first breakpoints — default styles target xl. Each @media rule + --region-color-north: #92a6b0; + --region-color-vale: #7ba3c4; + --region-color-riverlands: #6b89a8; + --region-color-westerlands: #c25c50; + --region-color-reach: #93a854; + --region-color-stormlands: #d3a94e; + --region-color-dorne: #cd7f4b; + --region-color-iron-islands: #6d9186; + --region-color-crownlands: #ad5c62; +} + +// Desktop-first breakpoints, default styles target xl. Each @media rule // overrides at the named breakpoint AND below. The SCSS breakpoint // variables in `styles/_breakpoints.scss` (`bp.$xs`, `bp.$sm`, ...) are // the source of truth; the `--bp-*` CSS custom properties above are @@ -56,28 +106,28 @@ html, body { margin: 0; padding: 0; + // Layered atmosphere: one warm lamp above and a grid of rivet heads + // punched into the soot-dark plating. background: radial-gradient( - ellipse at top left, - rgba(180, 140, 90, 0.15), - transparent 60% + 58% 40% at 50% -4%, + rgba(228, 191, 120, 0.06), + transparent 70% ), - radial-gradient( - ellipse at bottom right, - rgba(120, 80, 40, 0.18), - transparent 60% - ), - linear-gradient( - 180deg, - var(--parchment-light) 0%, - var(--parchment-dark) 100% - ); + radial-gradient(circle, rgba(190, 158, 110, 0.05) 1px, transparent 1.5px), + linear-gradient(180deg, var(--iron) 0%, var(--coal) 100%); + background-size: + auto, + 34px 34px, + auto; background-attachment: fixed; - color: var(--ink); + color: var(--gaslight); font-family: var(--font-body); font-size: 16px; + font-weight: 400; line-height: 1.65; min-height: 100vh; + scrollbar-color: var(--steel) transparent; @media (max-width: bp.$md) { font-size: 20px; @@ -100,9 +150,9 @@ h6 { padding: 0; display: grid; font-family: var(--font-heading); - color: var(--ink); - font-weight: 600; - letter-spacing: 1px; + color: var(--gaslight); + font-weight: 400; + letter-spacing: 0.01em; } p { @@ -111,19 +161,30 @@ p { font-size: 1.25rem; } +strong { + font-weight: 700; +} + h1 { - font-size: 2.2rem; - font-variant: small-caps; - letter-spacing: 2px; + font-size: 2rem; text-align: center; + color: var(--brass-bright); + line-height: 1.2; } + h2 { - font-size: 1.4rem; - border-bottom: 1px solid rgba(107, 68, 35, 0.3); + font-size: 1.2rem; + border-bottom: 3px double var(--edge-strong); + padding-bottom: 0.35rem; } + h3 { - font-size: 1.15rem; - color: var(--ink-faded); + font-family: var(--font-ui); + font-size: 0.9rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.18em; + color: var(--brass); } @media (max-width: bp.$sm) { @@ -136,25 +197,64 @@ h3 { } // Typographic primitive: italic page lede under the `h1`. Used by every -// `ParchmentLayout` page (`app/page.tsx`, houses, castles, not-found, etc.) -// — kept global alongside the heading rules above so consumers don't each +// `ParchmentLayout` page (`app/page.tsx`, houses, castles, not-found, etc.), +// kept global alongside the heading rules above so consumers don't each // import a typography module. .subtitle { display: grid; text-align: center; font-style: italic; - color: var(--ink-faded); + color: var(--gaslight-dim); padding: 0 0 1rem 0; margin: 0; - border-bottom: 1px solid rgba(107, 68, 35, 0.3); + border-bottom: 1px solid var(--edge); } a { - color: var(--ink-faded); + color: var(--brass-bright); text-decoration: underline; - text-decoration-color: rgba(107, 68, 35, 0.4); + text-decoration-color: rgba(176, 129, 58, 0.4); + transition: + color 140ms ease, + text-decoration-color 140ms ease; + + &:hover { + color: var(--verdigris); + text-decoration-color: rgba(121, 164, 140, 0.5); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; + } +} + +:focus-visible { + outline: 2px solid var(--verdigris); + outline-offset: 2px; +} + +::selection { + background: rgba(176, 129, 58, 0.35); + color: var(--gaslight); +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +::-webkit-scrollbar-thumb { + background: var(--steel); + border-radius: 5px; + border: 2px solid transparent; + background-clip: padding-box; &:hover { - color: var(--gold-leaf); + background-color: var(--brass-dark); + background-clip: padding-box; } } + +::-webkit-scrollbar-track { + background: transparent; +}