Added more social stuff like previewing gardens and stuff! Pretty

awesome.
This commit is contained in:
sarah committed 2026-09-07 18:40:32 +02:00
1 parent d1721517cc
commit 56d70e8c0c
9 files changed
+2623 -43

No files matched your search

+228
View File
@@ -0,0 +1,228 @@
<?php
$PageTitle = "Gardener";
$PageSection = "Social";
?>
<!DOCTYPE html>
<html lang="en" data-palette="CatppuccinMocha">
<?php require __DIR__ . "/Bits/Head.php"; ?>
<body>
<?php require __DIR__ . "/Bits/Navbar.php"; ?>
<main class="MainContainer">
<p class="GardenerBackLink">
<a href="/Pages/Social.html">Back to Social</a>
</p>
<div class="GardenerHero">
<div id="GardenerAvatar"></div>
<div class="GardenerHeroText">
<h1 id="GardenerName">Gardener</h1>
<p id="GardenerDescription">
Loading gardener...
</p>
</div>
</div>
<p
id="GardenerPageMessage"
class="PageMessage"
aria-live="polite"
>
Loading gardener...
</p>
<section
id="GardenerStatsSection"
class="Panel SocialSection"
hidden
>
<h2 class="PanelHeader PanelHeaderInset">Statistics</h2>
<div class="ProfileStatistics">
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Current Dew</span>
<strong id="GardenerCurrentDew">0</strong>
</div>
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Lifetime Dew</span>
<strong id="GardenerLifetimeDew">0</strong>
</div>
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Plants discovered</span>
<strong id="GardenerPlantsDiscovered">0</strong>
</div>
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Mutations discovered</span>
<strong id="GardenerMutationsDiscovered">0</strong>
</div>
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Gardens owned</span>
<strong id="GardenerGardensOwned">0</strong>
</div>
<div class="ProfileStatistic">
<span class="ProfileStatisticName">Active Garden size</span>
<strong id="GardenerActiveGardenSize">0×0</strong>
</div>
</div>
<p
id="GardenerStatsPrivateMessage"
class="ProfileMessage"
hidden
></p>
</section>
<section
id="GardenerGardensSection"
class="Panel SocialSection"
hidden
>
<div class="PanelHeader PanelHeaderInset GardenerSectionHeader">
<h2>Gardens</h2>
<span id="GardenerGardenPosition"></span>
</div>
<div
id="GardenerGardenControls"
class="GardenerGardenControls"
>
<button
id="PreviousGardenerGardenButton"
class="ActionButton"
type="button"
>
Previous
</button>
<div class="GardenerGardenTitle">
<strong id="GardenerGardenName">Garden</strong>
<span id="GardenerGardenSize">0×0</span>
</div>
<button
id="NextGardenerGardenButton"
class="ActionButton"
type="button"
>
Next
</button>
</div>
<div class="GardenGridViewport">
<div
id="GardenerGardenGrid"
class="GardenGrid GardenerGardenGrid"
></div>
</div>
<p
id="GardenerGardensMessage"
class="ProfileMessage"
></p>
</section>
<section
id="GardenerDonationSection"
class="Panel SocialSection InteractivePanel"
hidden
>
<h2 class="PanelHeader PanelHeaderInset">Dew donation</h2>
<p id="GardenerDonationBalance"></p>
<form
id="GardenerDonationForm"
class="FormRow"
>
<input
id="GardenerDonationAmount"
type="number"
min="1"
step="1"
inputmode="numeric"
placeholder="Dew amount"
required
>
<button type="submit">
Donate Dew
</button>
</form>
<p
id="GardenerDonationMessage"
class="ProfileMessage"
aria-live="polite"
></p>
</section>
<section
id="GardenerCommentsSection"
class="Panel SocialSection InteractivePanel"
hidden
>
<h2 class="PanelHeader PanelHeaderInset">Comments</h2>
<form id="GardenerCommentForm">
<label for="GardenerCommentInput">
Leave a comment
</label>
<textarea
id="GardenerCommentInput"
rows="3"
maxlength="1000"
placeholder="Write a comment..."
required
></textarea>
<div class="FormRow">
<button type="submit">
Leave comment
</button>
</div>
</form>
<p
id="GardenerCommentMessage"
class="ProfileMessage"
aria-live="polite"
></p>
<div
id="GardenerCommentList"
class="GardenerCommentList"
></div>
<button
id="LoadMoreGardenerCommentsButton"
class="ActionButton GardenerLoadMoreButton"
type="button"
hidden
>
Load more comments
</button>
</section>
</main>
<script src="/Scripts/Garden/PlantImages.js"></script>
<script src="/Scripts/Garden/Plants.js"></script>
<script src="/Scripts/Garden/Mutations.js"></script>
<script src="/Scripts/Garden/Save.js"></script>
<script src="/Scripts/Garden/Content.js"></script>
<script src="/Scripts/Garden/Users.js"></script>
<script src="/Scripts/Garden/Gardener.js"></script>
</body>
</html>
+42 -8
View File
@@ -312,11 +312,12 @@ $PageSection = "Guide";
<p>
Your <a href="/Pages/Profile.html">Profile</a>
contains your username, colour, Garden display settings,
statistics, and Account Key. Garden saves can sync through
the API, while the Account Key lets you use the same Garden
in another browser or on another device. It's basically
just like logging into an account.
contains your username, colour, Gardener description,
plant profile picture, privacy settings, Garden display
settings, statistics, and Account Key. Garden saves can
sync through the API, while the Account Key lets you use
the same Garden in another browser or on another device.
It's basically just like logging into an account.
</p>
<p>
@@ -325,6 +326,36 @@ $PageSection = "Guide";
</p>
</section>
<section
id="Social"
class="GuideSection"
>
<h2>Social and Gardener profiles</h2>
<p>
The <a href="/Pages/Social.html">Social</a> page lets
you search for other gardeners by username and open their
public Gardener page. Gardener profile pictures use a plant
and growth stage the player has discovered.
</p>
<p>
Gardener pages can show statistics and read-only previews
of every Garden the player owns. Players can independently
disable Garden previews, comments, Dew donations, and
public statistics from their Profile. Disabling public
statistics also hides the player from the Dew leaderboard.
</p>
<p>
Comments are simple messages left on a Gardener page. A
comment can be deleted by its author or by the owner of the
Gardener page. Dew donations transfer spendable Dew between
accounts, but donated Dew does not increase the recipient's
Lifetime Dew.
</p>
</section>
<section
id="Privacy"
class="GuideSection"
@@ -343,9 +374,12 @@ $PageSection = "Guide";
<p>
This browser stores your Account Key and a local copy
of your Garden save so the same account can keep working
between visits. If you set a username, some profile
information and statistics can also appear on the
public leaderboard.
between visits. If you set a username, your Gardener can
be found through Social. Your username, profile colour,
description, and selected plant profile picture form the
basic public Gardener profile; the separate privacy
settings control Garden previews, comments, Dew donations,
and public statistics.
</p>
<p>
+9 -7
View File
@@ -25,7 +25,7 @@ $PageTitle = "Profile";
<section
id="Player"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h2 class="PanelHeader PanelHeaderInset">Player</h2>
@@ -101,7 +101,7 @@ $PageTitle = "Profile";
<section
id="GardenerProfile"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h2 class="PanelHeader PanelHeaderInset">Gardener profile</h2>
@@ -226,6 +226,8 @@ $PageTitle = "Profile";
<p>
Choose which parts of your Gardener profile
other players will be able to use or view.
Hiding statistics also removes you from the
public Dew leaderboard.
</p>
<div class="ProfileButtonRow">
@@ -267,7 +269,7 @@ $PageTitle = "Profile";
<section
id="GardenDisplay"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h2 class="PanelHeader PanelHeaderInset">Garden display</h2>
@@ -411,7 +413,7 @@ $PageTitle = "Profile";
<section
id="Statistics"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h3 class="PanelHeader PanelHeaderInset">Statistics</h3>
@@ -481,7 +483,7 @@ $PageTitle = "Profile";
<section
id="Contact"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h2 class="PanelHeader PanelHeaderInset">Contact</h2>
@@ -501,7 +503,7 @@ $PageTitle = "Profile";
<section
id="Account"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h2 class="PanelHeader PanelHeaderInset">Account</h2>
@@ -610,7 +612,7 @@ $PageTitle = "Profile";
<section
id="Import"
class="Panel ProfileSection"
class="Panel ProfileSection InteractivePanel"
>
<h3 class="PanelHeader PanelHeaderInset">Import Account</h3>
+53 -1
View File
@@ -16,6 +16,54 @@ $PageSection = "Social";
<main class="MainContainer">
<h1>Social</h1>
<p>
Find other gardeners, visit their public profiles,
preview their Gardens, leave comments, and donate Dew
when they allow it.
</p>
<section
id="GardenerSearch"
class="Panel SocialSection"
>
<h2 class="PanelHeader PanelHeaderInset">Find gardeners</h2>
<form
id="GardenerSearchForm"
class="CatalogueControls"
role="search"
>
<input
id="GardenerSearchInput"
type="search"
maxlength="64"
placeholder="Search usernames..."
aria-label="Search gardeners by username"
autocomplete="off"
>
<button
class="ActionButton"
type="submit"
>
Search
</button>
</form>
<div
id="GardenerSearchResults"
class="GardenerSearchResults"
></div>
<p
id="GardenerSearchMessage"
class="PageMessage"
aria-live="polite"
>
Search for a gardener by username.
</p>
</section>
<section
id="Leaderboard"
class="Panel Leaderboard"
@@ -31,7 +79,7 @@ $PageSection = "Social";
</th>
<th>
Username
Gardener
</th>
<th class="LeaderboardDewColumn">
@@ -46,7 +94,11 @@ $PageSection = "Social";
</section>
</main>
<script src="/Scripts/Garden/PlantImages.js"></script>
<script src="/Scripts/Garden/Plants.js"></script>
<script src="/Scripts/Garden/Mutations.js"></script>
<script src="/Scripts/Garden/Save.js"></script>
<script src="/Scripts/Garden/Content.js"></script>
<script src="/Scripts/Garden/Users.js"></script>
<script src="/Scripts/Garden/Leaderboard.js"></script>
<script src="/Scripts/Garden/Social.js"></script>
File diff suppressed because it is too large. Load diff
+6 -6
View File
@@ -147,12 +147,12 @@ async function RenderLeaderboard(
Rank.textContent =
Player.Rank + ".";
Username.textContent =
Player.Username;
ApplyPlayerColour(
Username,
Player.Colour
Username.appendChild(
CreateGardenerIdentity(
Player.Username,
Player.Colour,
Player.ProfilePicture
)
);
Dew.textContent =
+312 -3
View File
@@ -1,5 +1,17 @@
let SocialCurrentUsername = null;
let GardenerSearchRequest = 0;
let GardenerSearchTimer = null;
async function StartSocial() {
let CurrentUsername = null;
try {
await LoadGameContent();
} catch (Error) {
console.error(
"Couldn't load Garden content for Social:",
Error
);
}
try {
const Profile = await GetProfile();
@@ -8,7 +20,7 @@ async function StartSocial() {
Profile.Success &&
Profile.Username !== null
) {
CurrentUsername =
SocialCurrentUsername =
Profile.Username;
}
} catch (Error) {
@@ -18,12 +30,309 @@ async function StartSocial() {
);
}
BindGardenerSearch();
await RenderLeaderboard(
CurrentUsername
SocialCurrentUsername
);
}
function BindGardenerSearch() {
const Form =
document.getElementById(
"GardenerSearchForm"
);
const Input =
document.getElementById(
"GardenerSearchInput"
);
if (
Form === null ||
Input === null
) {
return;
}
Form.addEventListener(
"submit",
Event => {
Event.preventDefault();
SearchGardeners(
Input.value
);
}
);
Input.addEventListener(
"input",
() => {
if (GardenerSearchTimer !== null) {
clearTimeout(
GardenerSearchTimer
);
}
const Search =
Input.value.trim();
if (Search.length === 0) {
ClearGardenerSearch();
return;
}
GardenerSearchTimer =
setTimeout(
() => {
SearchGardeners(
Search
);
},
250
);
}
);
const Query =
new URLSearchParams(
window.location.search
);
const InitialSearch =
Query.get("Search")?.trim() ?? "";
if (InitialSearch.length > 0) {
Input.value = InitialSearch;
SearchGardeners(
InitialSearch
);
}
}
function ClearGardenerSearch() {
GardenerSearchRequest++;
const Results =
document.getElementById(
"GardenerSearchResults"
);
Results?.replaceChildren();
SetGardenerSearchMessage(
"Search for a gardener by username."
);
}
async function SearchGardeners(
SearchValue
) {
const Search =
String(SearchValue ?? "")
.trim();
if (Search.length === 0) {
ClearGardenerSearch();
return;
}
const RequestId =
++GardenerSearchRequest;
SetGardenerSearchMessage(
"Searching..."
);
try {
const Query =
new URLSearchParams({
Search: Search
});
const Response = await fetch(
ApiUrl +
"/Gardeners.php?" +
Query.toString(),
{
cache: "no-store"
}
);
const Result =
await Response.json();
if (RequestId !== GardenerSearchRequest) {
return;
}
if (!Result.Success) {
throw new Error(
Result.Error ??
"Couldn't search gardeners."
);
}
RenderGardenerSearchResults(
Array.isArray(Result.Gardeners)
? Result.Gardeners
: []
);
} catch (Error) {
if (RequestId !== GardenerSearchRequest) {
return;
}
console.error(
"Couldn't search gardeners:",
Error
);
document.getElementById(
"GardenerSearchResults"
)?.replaceChildren();
SetGardenerSearchMessage(
"Couldn't search gardeners."
);
}
}
function RenderGardenerSearchResults(
Gardeners
) {
const Results =
document.getElementById(
"GardenerSearchResults"
);
if (Results === null) {
return;
}
Results.replaceChildren();
if (Gardeners.length === 0) {
SetGardenerSearchMessage(
"No gardeners matched that search."
);
return;
}
for (const Gardener of Gardeners) {
Results.appendChild(
CreateGardenerSearchResult(
Gardener
)
);
}
SetGardenerSearchMessage(
Gardeners.length === 1
? "1 gardener found."
: Gardeners.length
.toLocaleString() +
" gardeners found."
);
}
function CreateGardenerSearchResult(
Gardener
) {
const Link =
document.createElement(
"a"
);
Link.className =
"PlantTile GardenerSearchResult";
Link.href =
GetGardenerPageUrl(
Gardener.Username
);
const Avatar =
CreateGardenerAvatar(
Gardener.ProfilePicture,
Gardener.Username,
"GardenerAvatarSearch"
);
const Details =
document.createElement(
"span"
);
Details.className =
"GardenerSearchResultDetails";
const Name =
document.createElement(
"strong"
);
Name.className =
"GardenerSearchResultName";
Name.textContent =
Gardener.Username;
ApplyPlayerColour(
Name,
Gardener.Colour
);
const Description =
document.createElement(
"span"
);
Description.className =
"GardenerSearchResultDescription";
Description.textContent =
typeof Gardener.Description ===
"string" &&
Gardener.Description.length > 0
? Gardener.Description
: "No description.";
Details.append(
Name,
Description
);
Link.append(
Avatar,
Details
);
return Link;
}
function SetGardenerSearchMessage(
Text
) {
const Message =
document.getElementById(
"GardenerSearchMessage"
);
if (Message !== null) {
Message.textContent = Text;
}
}
document.addEventListener(
"DOMContentLoaded",
StartSocial
+213
View File
@@ -191,6 +191,219 @@ function ApplyPlayerColour(
}
function GetGardenerProfilePictureSource(
ProfilePicture
) {
if (
ProfilePicture === null ||
typeof ProfilePicture !== "object" ||
Array.isArray(ProfilePicture) ||
typeof ProfilePicture.PlantKey !==
"string" ||
!Number.isInteger(
Number(
ProfilePicture.GrowthStage
)
)
) {
return null;
}
if (
typeof Plants === "undefined" ||
typeof GetPlantImageSources !==
"function"
) {
return null;
}
const Plant =
Plants[
ProfilePicture.PlantKey
];
if (Plant === undefined) {
return null;
}
const Images =
GetPlantImageSources(
Plant
);
const GrowthStage = Number(
ProfilePicture.GrowthStage
);
const RequestedSource =
Images[GrowthStage - 1];
if (
typeof RequestedSource ===
"string" &&
RequestedSource.length > 0
) {
return RequestedSource;
}
for (
let Index = Images.length - 1;
Index >= 0;
Index--
) {
if (
typeof Images[Index] ===
"string" &&
Images[Index].length > 0
) {
return Images[Index];
}
}
return null;
}
function CreateGardenerAvatar(
ProfilePicture,
Username = "",
AdditionalClass = null
) {
const Avatar =
document.createElement(
"span"
);
Avatar.className =
"PlantTile GardenerAvatar";
if (
typeof AdditionalClass ===
"string" &&
AdditionalClass.length > 0
) {
Avatar.classList.add(
AdditionalClass
);
}
if (Username.length > 0) {
Avatar.title =
Username +
"'s profile picture";
}
const ImageSource =
GetGardenerProfilePictureSource(
ProfilePicture
);
if (ImageSource !== null) {
const Image =
document.createElement(
"img"
);
Image.className =
"PlantSprite";
Image.src = ImageSource;
Image.alt = "";
Image.draggable = false;
Avatar.appendChild(
Image
);
return Avatar;
}
const Placeholder =
document.createElement(
"span"
);
Placeholder.className =
"GardenerAvatarPlaceholder";
Placeholder.textContent = "□";
Avatar.appendChild(
Placeholder
);
return Avatar;
}
function GetGardenerPageUrl(
Username
) {
const Query =
new URLSearchParams({
Username: Username
});
return (
"/Pages/Gardener.html?" +
Query.toString()
);
}
function CreateGardenerIdentity(
Username,
Colour,
ProfilePicture,
LinkToGardener = true
) {
const Container =
document.createElement(
LinkToGardener
? "a"
: "span"
);
Container.className =
"GardenerIdentity";
if (LinkToGardener) {
Container.href =
GetGardenerPageUrl(
Username
);
}
const Avatar =
CreateGardenerAvatar(
ProfilePicture,
Username
);
const Name =
document.createElement(
"span"
);
Name.className =
"GardenerIdentityName";
Name.textContent = Username;
ApplyPlayerColour(
Name,
Colour
);
Container.append(
Avatar,
Name
);
return Container;
}
async function GetProfile() {
const Response = await fetch(
ApiUrl + "/Profile.php",
+276 -18
View File
@@ -1020,7 +1020,7 @@ input, button, textarea, select {
text-align: right;
}
.ProfileSection {
.ProfileSection, .SocialSection {
margin-top: 1.5rem;
padding: 0.75rem;
}
@@ -1029,7 +1029,7 @@ input, button, textarea, select {
margin-top: 1.5rem;
}
.ProfileFormRow, .ProfileButtonRow {
.FormRow, .ProfileFormRow, .ProfileButtonRow {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
@@ -1040,13 +1040,13 @@ input, button, textarea, select {
margin-top: 0.75rem;
}
.ProfileSection label {
.InteractivePanel label {
display: block;
margin-bottom: 0.5rem;
color: var(--Blue);
}
.ProfileSection input, .ProfileSection textarea, .ProfileSection select, .ProfileSection button {
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select, .InteractivePanel button {
font-size: 1rem;
color: var(--Text);
background-color: var(--Surface0);
@@ -1055,46 +1055,46 @@ input, button, textarea, select {
appearance: none;
}
.ProfileSection input, .ProfileSection textarea, .ProfileSection select {
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select {
padding: 0.6rem;
outline: none;
}
.ProfileSection input {
.InteractivePanel input {
min-width: 12rem;
}
.ProfileSection select {
.InteractivePanel select {
appearance: auto;
}
.ProfileSection textarea {
.InteractivePanel textarea {
width: 100%;
max-width: 24rem;
resize: vertical;
}
.ProfileSection input::placeholder, .ProfileSection textarea::placeholder {
.InteractivePanel input::placeholder, .InteractivePanel textarea::placeholder {
color: var(--Overlay1);
}
.ProfileSection input:hover, .ProfileSection textarea:hover, .ProfileSection select:hover {
.InteractivePanel input:hover, .InteractivePanel textarea:hover, .InteractivePanel select:hover {
background-color: var(--Surface1);
border-color: var(--Overlay2);
}
.ProfileSection input:focus, .ProfileSection textarea:focus, .ProfileSection select:focus {
.InteractivePanel input:focus, .InteractivePanel textarea:focus, .InteractivePanel select:focus {
background-color: var(--Surface1);
border-color: var(--Sky);
}
.ProfileSection button {
.InteractivePanel button {
padding: 0.6rem 0.8rem;
cursor: pointer;
box-shadow: 3px 3px 0 var(--Crust);
}
.ProfileSection button:hover {
.InteractivePanel button:hover:not(:disabled) {
color: var(--Sky);
background-color: var(--Surface1);
border-color: var(--Sky);
@@ -1102,7 +1102,7 @@ input, button, textarea, select {
box-shadow: 2px 2px 0 var(--Crust);
}
.ProfileSection button:disabled {
.InteractivePanel button:disabled {
cursor: not-allowed;
color: var(--Overlay1);
background-color: var(--Surface0);
@@ -1110,7 +1110,7 @@ input, button, textarea, select {
box-shadow: none;
}
.ProfileSection button:active {
.InteractivePanel button:active:not(:disabled) {
color: var(--Green);
background-color: var(--Surface2);
border-color: var(--Green);
@@ -1118,7 +1118,7 @@ input, button, textarea, select {
box-shadow: 1px 1px 0 var(--Crust);
}
.ProfileSection button:focus-visible, .ProfileSection summary:focus-visible {
.InteractivePanel button:focus-visible, .InteractivePanel summary:focus-visible {
outline: 3px solid var(--Yellow);
outline-offset: 3px;
}
@@ -1259,12 +1259,12 @@ input, button, textarea, select {
overflow-wrap: anywhere;
}
.ProfileFormRow, .ProfileButtonRow {
.FormRow, .ProfileFormRow, .ProfileButtonRow {
flex-direction: column;
align-items: stretch;
}
.ProfileSection input, .ProfileSection textarea, .ProfileSection select, .ProfileSection button {
.InteractivePanel input, .InteractivePanel textarea, .InteractivePanel select, .InteractivePanel button {
width: 100%;
}
@@ -1609,6 +1609,264 @@ input, button, textarea, select {
}
}
/* * Garden social */
.GardenerAvatar {
display: flex;
width: 3rem;
height: 3rem;
flex: 0 0 3rem;
align-items: center;
justify-content: center;
}
.GardenerAvatarPlaceholder {
color: var(--Overlay1);
font-size: 1.75rem;
line-height: 1;
}
.GardenerIdentity {
display: inline-flex;
min-width: 0;
gap: 0.65rem;
align-items: center;
color: inherit;
text-decoration: none;
}
.GardenerIdentityName {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.GardenerIdentity:hover .GardenerIdentityName {
text-decoration: underline;
}
.GardenerSearchResults {
display: grid;
gap: 0.75rem;
}
.GardenerSearchResult {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 0.85rem;
padding: 0.75rem;
align-items: center;
color: var(--Text);
text-decoration: none;
box-shadow: 3px 3px 0 var(--Crust);
}
.GardenerSearchResult:hover {
color: var(--Text);
background-color: var(--Surface1);
border-color: var(--Sky);
transform: translate(1px, 1px);
box-shadow: 2px 2px 0 var(--Crust);
}
.GardenerAvatarSearch {
width: 4.25rem;
height: 4.25rem;
flex-basis: 4.25rem;
}
.GardenerSearchResultDetails {
display: flex;
min-width: 0;
flex-direction: column;
gap: 0.35rem;
}
.GardenerSearchResultName {
overflow: hidden;
font-size: 1.35rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.GardenerSearchResultDescription {
display: -webkit-box;
overflow: hidden;
color: var(--Subtext1);
line-height: 1.35;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.GardenerBackLink {
margin-bottom: 1rem;
}
.GardenerHero {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
gap: 1rem;
align-items: center;
}
.GardenerHeroText {
min-width: 0;
}
.GardenerHeroText h1 {
margin-bottom: 0.5rem;
}
.GardenerHeroText p {
margin-bottom: 0;
white-space: pre-wrap;
}
.GardenerAvatarHero {
width: 7rem;
height: 7rem;
flex-basis: 7rem;
}
.GardenerSectionHeader {
justify-content: space-between;
}
.GardenerGardenControls {
display: grid;
grid-template-columns: minmax(7rem, auto) minmax(0, 1fr) minmax(7rem, auto);
gap: 0.75rem;
margin-bottom: 1rem;
align-items: center;
}
.GardenerGardenTitle {
display: flex;
min-width: 0;
flex-direction: column;
gap: 0.2rem;
align-items: center;
text-align: center;
}
.GardenerGardenTitle strong {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.GardenerGardenTitle span,
.GardenerSectionHeader span {
color: var(--Subtext1);
}
.GardenPlotReadOnly,
.GardenPlotReadOnly:hover {
cursor: default;
transform: none;
box-shadow: 3px 3px 0 var(--Crust);
}
.GardenPlotReadOnly:hover {
--TileBackground: var(--Surface0);
--PlotBorderColour: var(--Surface2);
}
.GardenPlotReadOnly.GardenPlotEmpty:hover {
--TileBackground: var(--Mantle);
}
.GardenPlotReadOnly.GardenPlotMature:hover {
--GrowthColour: var(--Green);
}
.GardenerCommentList {
display: grid;
gap: 0.75rem;
margin-top: 1rem;
}
.GardenerComment {
padding: 0.75rem;
}
.GardenerCommentHeader {
display: flex;
gap: 0.75rem;
align-items: center;
justify-content: space-between;
}
.GardenerCommentTime {
flex: 0 0 auto;
color: var(--Overlay1);
font-size: 0.85rem;
text-align: right;
}
.GardenerCommentText {
margin: 0.75rem 0 0;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.InteractivePanel .GardenerCommentDelete {
width: auto;
margin-top: 0.75rem;
}
.GardenerCommentsEmpty {
margin: 0;
color: var(--Subtext1);
}
.GardenerLoadMoreButton {
margin-top: 0.75rem;
}
.LeaderboardTable {
font-size: 1.35rem;
}
.LeaderboardUsername .GardenerAvatar {
width: 3.25rem;
height: 3.25rem;
flex-basis: 3.25rem;
}
.LeaderboardUsername .GardenerIdentity {
width: 100%;
}
@media (max-width: 600px) {
.GardenerAvatarHero {
width: 5.5rem;
height: 5.5rem;
flex-basis: 5.5rem;
}
.GardenerGardenControls {
grid-template-columns: 1fr 1fr;
}
.GardenerGardenTitle {
grid-column: 1 / -1;
grid-row: 1;
}
.GardenerCommentHeader {
align-items: flex-start;
flex-direction: column;
}
.GardenerCommentTime {
text-align: left;
}
}
/* * Garden guide */
.GuideSection {