Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,54 @@
name: Savely
description: A warm, patient, local-first savings app — the Warm Meadow system
colors:
# light / dark — every token is trait-aware (Color+Warm.swift). Light is normative for the look; dark is "the notebook under a lamp".
bg: "#f6f4ee"
bg-dark: "#161512"
surface: "#ffffff"
surface-dark: "#262420"
ink: "#1a1a17"
ink-dark: "#f2efe7"
ink-soft: "#55524c"
ink-muted: "#8c8880"
ink-soft-dark: "#c6c1b5"
ink-muted: "#746f66"
ink-muted-dark: "#9d978a"
line: "rgba(30, 25, 15, 0.08)"
line-dark: "rgba(242, 239, 231, 0.10)"
line-soft: "rgba(30, 25, 15, 0.04)"
line-soft-dark: "rgba(242, 239, 231, 0.05)"
green: "#2f6b4a"
green-dark: "#78b58f"
green-fill: "#2f6b4a"
green-fill-dark: "#367c56"
green-deep: "#1f4a33"
green-deep-dark: "#b6dcc4"
green-soft: "#e8f0ea"
green-soft-dark: "#22382c"
green-tint: "#f2f7f3"
amber: "#c48a2a"
green-tint-dark: "#1b2a21"
amber: "#a8741c"
amber-dark: "#dea64a"
amber-deep: "#7a5618"
amber-deep-dark: "#ebc27a"
amber-soft: "#f6ecd6"
amber-soft-dark: "#3a2f19"
clay: "#b85c42"
clay-dark: "#d9846a"
clay-soft: "#f6e1d8"
clay-soft-dark: "#3d2721"
sky: "#4a7ba6"
sky-dark: "#7ea9d0"
sky-soft: "#dce8f2"
sky-soft-dark: "#1e2d3b"
lilac: "#7d6ab8"
lilac-dark: "#b3a4dc"
lilac-soft: "#ece8f4"
lilac-soft-dark: "#2e2740"
on-green: "#ffffff"
on-ink: "#ffffff"
on-ink-dark: "#161512"
track: "#ede6d4"
track-dark: "#3a362e"
typography:
display:
fontFamily: "system serif (New York)"
Expand Down Expand Up @@ -66,13 +97,13 @@ spacing:
hero: "24pt"
components:
button-primary:
backgroundColor: "{colors.green}"
textColor: "#ffffff"
backgroundColor: "{colors.green-fill}"
textColor: "{colors.on-green}"
rounded: "{rounded.control}"
height: "48–50pt"
button-primary-dark-shell:
backgroundColor: "{colors.ink}"
textColor: "#ffffff"
textColor: "{colors.on-ink}"
rounded: "18pt"
size: "52pt"
button-icon-outline:
Expand Down Expand Up @@ -141,16 +172,17 @@ KPI grids and pie charts; cold gray-on-white spreadsheets.

## Colors

A warm neutral ground with a single committed green; everything else is a soft tint that holds an icon.
A warm neutral ground with a single committed green; everything else is a soft tint that holds an icon. Every token is trait-aware: dark mode is the same notebook under a lamp — same hues at low chroma, ink flips light, the `-soft` tints become deep tints of their own hue, and the one green splits into *ink* (brightens) and *fill* (stays deep). Depth in dark is the hairline (`line-dark`, warm white 10%), exactly as in light.

### Primary
- **Meadow Green** (`green`, #2f6b4a): the only saturated color. Primary buttons, income figures, active tab, progress rings, selected-state text. It is what "growing" looks like.
- **Meadow Green** (`green`, #2f6b4a): the only saturated color, used *as ink* — income figures, active tab, progress rings and bars, selected-state text, toggle tint. It brightens to #78b58f in dark. It is what "growing" looks like.
- **Meadow Green Fill** (`green-fill`, #2f6b4a light / #367c56 dark): green *as a surface* — primary buttons, the income card, the Planted screen, the selected radio. Stays deep in dark so its white content (`on-green`) keeps ≥5:1.
- **Meadow Green Deep** (`green-deep`, #1f4a33): text on green-soft chips and banners; darker end of the ring gradient.
- **Meadow Green Soft** (`green-soft`, #e8f0ea): selected chip fill, income icon tile, the auto-move banner. Never text.
- **Meadow Green Tint** (`green-tint`, #f2f7f3): selected row background in goal pickers; the tip-of-the-day card. The quietest possible "this one".

### Secondary (category tints — icons and chips only)
- **Amber** (`amber`, #c48a2a) on **Amber Soft** (`amber-soft`, #f6ecd6): coffee / expense category, the "favorite" star, the identity monogram tile.
- **Amber** (`amber`, #a8741c) on **Amber Soft** (`amber-soft`, #f6ecd6): coffee / expense category, the "favorite" star. Was #c48a2a (2.6:1 on its tile); corrected in PR D2 to 3.5:1 for glyphs. **Amber Deep** (`amber-deep`, #7a5618) is amber as *text* on an amber-soft banner.
- **Clay** (`clay`, #b85c42) on **Clay Soft** (`clay-soft`, #f6e1d8): shopping / receipt scanning tile; the *only* negative-trend color (income badge going down).
- **Sky** (`sky`, #4a7ba6) on **Sky Soft** (`sky-soft`, #dce8f2): goals / transit tile.

Expand All @@ -159,7 +191,7 @@ A warm neutral ground with a single committed green; everything else is a soft t
- **Paper** (`surface`, #ffffff): cards, rows, action sheets, tab bar. Sits on the ground with a hairline, never a shadow.
- **Warm Ink** (`ink`, #1a1a17): all display and title type; primary body; the dark "+" shell and the scan-receipt banner.
- **Warm Ink Soft** (`ink-soft`, #55524c): body copy under a title, unselected chip text, settings icons.
- **Warm Ink Muted** (`ink-muted`, #8c8880): metadata — dates, "August · $500", uppercase section labels, placeholders. **Measured 3.2:1 on ground / 3.5:1 on paper: passes AA only as large text (≥18pt or bold ≥14pt).** Its current use at 12–13pt regular is a known AA gap, tracked in `docs/plans/pr-d-profile.md`.
- **Warm Ink Muted** (`ink-muted`, #746f66): metadata — dates, "August · $500", uppercase section labels, placeholders. Was #8c8880 (3.2:1, failing AA at 12–13pt); corrected in PR D2 to 4.5:1 on the ground / 5.0 on paper — same warm-ink family, one step darker.
- **Hairline** (`line`, 8% warm black) and **Hairline Soft** (`line-soft`, 4%): card borders and row dividers respectively. This *is* the depth system.

### Named Rules
Expand Down Expand Up @@ -270,5 +302,5 @@ A 36–44pt rounded square (10–12pt radius) in a `-soft` tint holding one 14
- **Don't** fill a button with amber, clay or sky, and don't use a `-soft` tint as a card background.
- **Don't** bold the serif or use it below 18pt.
- **Don't** introduce gradients, glass, or a second accent hue.
- **Don't** hardcode `.white` / `Color(red:…)` in a view — every color goes through `Color+Warm.swift`, which is the single place a dark scheme can be introduced.
- **Don't** hardcode `.white` / `Color(red:…)` in a view — every color goes through `Color+Warm.swift`, where each token carries its light and dark value. White content on a green fill is `on-green`; on an ink fill it is `on-ink` (which flips to the ground in dark). The only literals left are deliberate and commented: the splash frame, the sprout mark, camera-overlay controls, the PDF.
- **Don't** invent a new kicker or eyebrow style; the existing 11pt uppercase tracked label is the one and only.
24 changes: 4 additions & 20 deletions Savely.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
1DC014132CE5B0C5009A46E1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC014122CE5B0C5009A46E1 /* ContentView.swift */; };
1DC258082CC07DCC0002DDE3 /* SavelyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC258072CC07DCC0002DDE3 /* SavelyApp.swift */; };
1DC2580C2CC07DCF0002DDE3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1DC2580B2CC07DCF0002DDE3 /* Assets.xcassets */; };
A11A000AC0DE5AFE1000000A /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = A11A0009C0DE5AFE10000009 /* AppIcon.icon */; };
1DC2580F2CC07DCF0002DDE3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1DC2580E2CC07DCF0002DDE3 /* Preview Assets.xcassets */; };
1DC258192CC07DCF0002DDE3 /* SavelyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC258182CC07DCF0002DDE3 /* SavelyTests.swift */; };
1DC258232CC07DCF0002DDE3 /* SavelyUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC258222CC07DCF0002DDE3 /* SavelyUITests.swift */; };
Expand All @@ -61,16 +62,13 @@
1DE4B4D02CE5F2D6006C8F65 /* OnboardingData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4CF2CE5F2D6006C8F65 /* OnboardingData.swift */; };
1DE4B4D42CE60032006C8F65 /* HybridTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4D32CE60032006C8F65 /* HybridTextField.swift */; };
1DE4B4D62CE7AF93006C8F65 /* CustomTextfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4D52CE7AF93006C8F65 /* CustomTextfield.swift */; };
1DE4B4D82CE7B10E006C8F65 /* PrimaryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4D72CE7B10E006C8F65 /* PrimaryButton.swift */; };
1DE4B4EC2CEC4EE3006C8F65 /* AppViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4EB2CEC4EE3006C8F65 /* AppViewModel.swift */; };
1DE4B4EF2CEC514B006C8F65 /* ProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE4B4EE2CEC514B006C8F65 /* ProfileViewModel.swift */; };
1DE9FE0E2D063AF20069AA64 /* NotificationSettingsStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE9FE0D2D063AF20069AA64 /* NotificationSettingsStepView.swift */; };
1DE9FE102D063BC80069AA64 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE9FE0F2D063BC80069AA64 /* NotificationManager.swift */; };
1DE9FE122D0650150069AA64 /* NotificationTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE9FE112D0650150069AA64 /* NotificationTimePicker.swift */; };
1DE9FE152D0664A60069AA64 /* ReportsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE9FE142D0664A60069AA64 /* ReportsViewModel.swift */; };
1DE9FE172D066EE20069AA64 /* ReportsPDFGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DE9FE162D066EE20069AA64 /* ReportsPDFGenerator.swift */; };
1DEACCDE2D0BE6340084CC39 /* NetworkErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DEACCDD2D0BE6340084CC39 /* NetworkErrorView.swift */; };
1DF579C52F673A8B009A41B3 /* InsightRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF579C42F673A8B009A41B3 /* InsightRow.swift */; };
1DF7228E2F9C182300320B38 /* Color+Warm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF7228D2F9C182300320B38 /* Color+Warm.swift */; };
1DF722902F9C18FE00320B38 /* GoalDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF7228F2F9C18FE00320B38 /* GoalDetailView.swift */; };
1DF722922F9C19CE00320B38 /* MoneyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF722912F9C19CE00320B38 /* MoneyView.swift */; };
Expand Down Expand Up @@ -138,6 +136,7 @@
1DC258042CC07DCC0002DDE3 /* Savely.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Savely.app; sourceTree = BUILT_PRODUCTS_DIR; };
1DC258072CC07DCC0002DDE3 /* SavelyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavelyApp.swift; sourceTree = "<group>"; };
1DC2580B2CC07DCF0002DDE3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A11A0009C0DE5AFE10000009 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
1DC2580E2CC07DCF0002DDE3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
1DC258142CC07DCF0002DDE3 /* SavelyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SavelyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
1DC258182CC07DCF0002DDE3 /* SavelyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavelyTests.swift; sourceTree = "<group>"; };
Expand All @@ -156,17 +155,14 @@
1DE4B4CF2CE5F2D6006C8F65 /* OnboardingData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingData.swift; sourceTree = "<group>"; };
1DE4B4D32CE60032006C8F65 /* HybridTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HybridTextField.swift; sourceTree = "<group>"; };
1DE4B4D52CE7AF93006C8F65 /* CustomTextfield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextfield.swift; sourceTree = "<group>"; };
1DE4B4D72CE7B10E006C8F65 /* PrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryButton.swift; sourceTree = "<group>"; };
1DE4B4EA2CEC037E006C8F65 /* Savely.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Savely.entitlements; sourceTree = "<group>"; };
1DE4B4EB2CEC4EE3006C8F65 /* AppViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewModel.swift; sourceTree = "<group>"; };
1DE4B4EE2CEC514B006C8F65 /* ProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewModel.swift; sourceTree = "<group>"; };
1DE9FE0D2D063AF20069AA64 /* NotificationSettingsStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsStepView.swift; sourceTree = "<group>"; };
1DE9FE0F2D063BC80069AA64 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
1DE9FE112D0650150069AA64 /* NotificationTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTimePicker.swift; sourceTree = "<group>"; };
1DE9FE142D0664A60069AA64 /* ReportsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsViewModel.swift; sourceTree = "<group>"; };
1DE9FE162D066EE20069AA64 /* ReportsPDFGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportsPDFGenerator.swift; sourceTree = "<group>"; };
1DEACCDD2D0BE6340084CC39 /* NetworkErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkErrorView.swift; sourceTree = "<group>"; };
1DF579C42F673A8B009A41B3 /* InsightRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsightRow.swift; sourceTree = "<group>"; };
1DF7228D2F9C182300320B38 /* Color+Warm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Warm.swift"; sourceTree = "<group>"; };
1DF7228F2F9C18FE00320B38 /* GoalDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoalDetailView.swift; sourceTree = "<group>"; };
1DF722912F9C19CE00320B38 /* MoneyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoneyView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -262,14 +258,6 @@
path = Utilities;
sourceTree = "<group>";
};
1DAF5D012CED9475001C0AF3 /* Buttons */ = {
isa = PBXGroup;
children = (
1DE4B4D72CE7B10E006C8F65 /* PrimaryButton.swift */,
);
path = Buttons;
sourceTree = "<group>";
};
1DAF5D022CED9481001C0AF3 /* CameraScanner */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -381,7 +369,6 @@
1DC014042CDE00F7009A46E1 /* ProfileTab */ = {
isa = PBXGroup;
children = (
1DE9FE112D0650150069AA64 /* NotificationTimePicker.swift */,
1DAF5D1F2CEE9767001C0AF3 /* TipHistoryView.swift */,
1DC2583B2CC081D90002DDE3 /* ProfileView.swift */,
1DF722932F9C19E600320B38 /* AchievementsView.swift */,
Expand Down Expand Up @@ -434,6 +421,7 @@
1D3669842CC62EDD00E84FF9 /* ViewModels */,
1DC258072CC07DCC0002DDE3 /* SavelyApp.swift */,
1DC2580B2CC07DCF0002DDE3 /* Assets.xcassets */,
A11A0009C0DE5AFE10000009 /* AppIcon.icon */,
1DC2580D2CC07DCF0002DDE3 /* Preview Content */,
1DAF5D212CEE9989001C0AF3 /* Info.plist */,
1DAF5D232CEE9AF4001C0AF3 /* Config.plist */,
Expand Down Expand Up @@ -492,12 +480,10 @@
isa = PBXGroup;
children = (
F38DAD27B5231BDC080F36DA /* SproutMark.swift */,
1DF579C42F673A8B009A41B3 /* InsightRow.swift */,
1DEACCDD2D0BE6340084CC39 /* NetworkErrorView.swift */,
1DAF5D042CED94F1001C0AF3 /* Tips */,
1DAF5D032CED94DD001C0AF3 /* TextFields */,
1DAF5D022CED9481001C0AF3 /* CameraScanner */,
1DAF5D012CED9475001C0AF3 /* Buttons */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -642,6 +628,7 @@
files = (
1DC2580F2CC07DCF0002DDE3 /* Preview Assets.xcassets in Resources */,
1DC2580C2CC07DCF0002DDE3 /* Assets.xcassets in Resources */,
A11A000AC0DE5AFE1000000A /* AppIcon.icon in Resources */,
1DAF5D242CEE9AF4001C0AF3 /* Config.plist in Resources */,
1D36698B2CC62FD700E84FF9 /* Localizable.xcstrings in Resources */,
);
Expand Down Expand Up @@ -700,7 +687,6 @@
1DC014132CE5B0C5009A46E1 /* ContentView.swift in Sources */,
1DE4B4D42CE60032006C8F65 /* HybridTextField.swift in Sources */,
1DC013FB2CDB575F009A46E1 /* IncomesTrackerView.swift in Sources */,
1DE4B4D82CE7B10E006C8F65 /* PrimaryButton.swift in Sources */,
1D3669872CC62F8200E84FF9 /* Strings.swift in Sources */,
1DF7228E2F9C182300320B38 /* Color+Warm.swift in Sources */,
1DE9FE0E2D063AF20069AA64 /* NotificationSettingsStepView.swift in Sources */,
Expand All @@ -710,13 +696,11 @@
1DC258082CC07DCC0002DDE3 /* SavelyApp.swift in Sources */,
1DF722962F9C2FA400320B38 /* AddGoalFlow.swift in Sources */,
1DAF5D202CEE9767001C0AF3 /* TipHistoryView.swift in Sources */,
1DF579C52F673A8B009A41B3 /* InsightRow.swift in Sources */,
82D33CE42CEC66F200BE005E /* TotalConfirmationView.swift in Sources */,
1DE9FE172D066EE20069AA64 /* ReportsPDFGenerator.swift in Sources */,
1D3669892CC62F9100E84FF9 /* UIConstants.swift in Sources */,
1DAF5D0C2CEDA6C0001C0AF3 /* IncomesTrackerViewModel.swift in Sources */,
1DF722922F9C19CE00320B38 /* MoneyView.swift in Sources */,
1DE9FE122D0650150069AA64 /* NotificationTimePicker.swift in Sources */,
1D88B7D12CC6EBB4009B6538 /* CameraView.swift in Sources */,
1DEACCDE2D0BE6340084CC39 /* NetworkErrorView.swift in Sources */,
1DAF5D1B2CEDC118001C0AF3 /* GoalsViewModel.swift in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions Savely/AppIcon.icon/Assets/sprout-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions Savely/AppIcon.icon/Assets/sprout-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions Savely/AppIcon.icon/Assets/sprout-mono.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions Savely/AppIcon.icon/icon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"fill": {
"linear-gradient": [
"srgb:0.980,0.973,0.953,1.000",
"srgb:0.933,0.918,0.878,1.000"
],
"specializations": [
{
"appearance": "dark",
"value": {
"solid": "srgb:0.153,0.220,0.176,1.000"
}
}
]
},
"groups": [
{
"layers": [
{
"image-name": "sprout-light.svg",
"name": "sprout",
"specializations": [
{
"appearance": "dark",
"value": {
"image-name": "sprout-dark.svg"
}
},
{
"appearance": "tinted",
"value": {
"image-name": "sprout-mono.svg"
}
}
]
}
],
"shadow": {
"kind": "neutral",
"opacity": 0.3
},
"translucency": {
"enabled": false,
"value": 0.0
}
}
],
"supported-platforms": {
"circles": [
"watchOS"
],
"squares": "shared"
}
}
Loading
Loading