#include "common.h"

// ============================================================================
//  GUILD SYSTEM
//  - one active guild per player (joinedGuild)
//  - guilds hold up to MAX_GUILD_MEMBERS randomly-drawn NPC members
//  - each guild carries a customizable set of attribute / skill / stat reqs
//  - three-option menu: [Your Guild] Menu · Join / Switch · Create
// ============================================================================

// A single join-requirement: player's <attribute|skill|stat>[index] must be >= minValue
typedef enum { REQ_ATTR = 0, REQ_SKILL = 1, REQ_STAT = 2 } ReqKind;

typedef struct {
    ReqKind kind;
    int     index;      // index into attributes_names / skill_names / stats_names
    int     minValue;   // player's value must be >= this to qualify
} GuildReq;

typedef struct {
    char     name[MAX_GNAME];
    int      reqCount;
    GuildReq reqs[MAX_GUILD_REQS];
    int      memberCount;                            // number of NPC members currently on the roster
    char     members[MAX_GUILD_MEMBERS][MAX_GNAME];  // randomly drawn NPC roster
    int      leaderIdx;                              // -1 = you are leader, else index into members[]  (only meaningful while joined)
} Guild;

// Layout constants for the guild tab (drawn inside the 900x600 content panel)
#define G_W        672     // content width used by guild views
#define G_BOTTOM   566     // last usable content row (app footer sits at 572)

// ---- Guild state ----
static Guild guilds[MAX_GUILDS];
static int   guildCount   = 0;
int          guild_view   = 0;                        // 0 menu · 1 your-guild · 2 join/switch · 3 create
static int   joinedGuild  = -1;                       // index into guilds[], or -1 if none

static float guildScroll  = 0.f;                      // roster scroll (your-guild view)
static float joinScroll   = 0.f;                      // guild-list scroll (join view)
static int   deleteConfirmGuild = -1;                 // join view: guild index showing the delete confirmation, or -1
static float createScroll = 0.f;                      // requirement-list scroll (create view)

int          guild_req_modal = -1;                    // index of guild whose requirements modal is open, or -1
static float reqModalScroll  = 0.f;                   // requirements modal: list scroll

// Create-guild working buffers (committed only when "Create Guild" is pressed)
static TBox  tbGuildName  = {{0}, false};
static int   newReqAttr [ATTRIBUTES_COUNT] = {0};
static int   newReqSkill[SKILL_COUNT]      = {0};
static int   newReqStat [STATS_COUNT]      = {0};
static int   createCat    = 0;                        // 0 attributes · 1 skills · 2 stats
static int   newMemberCount = -1;                     // desired NPC roster size for the new guild, -1 = random

// ---- Requirement helpers ---------------------------------------------------

static int ReqCatCount(int cat)
{
    if(cat==0) return ATTRIBUTES_COUNT;
    if(cat==1) return SKILL_COUNT;
    return STATS_COUNT;
}

static const char *ReqCatName(int cat)
{
    return cat==0 ? "Attributes" : cat==1 ? "Skills" : "Stats";
}

static const char *ReqItemName(ReqKind kind, int index)
{
    if(kind==REQ_ATTR)  return (index>=0&&index<ATTRIBUTES_COUNT)? attributes_names[index] : "?";
    if(kind==REQ_SKILL) return (index>=0&&index<SKILL_COUNT)     ? skill_names[index]      : "?";
    return (index>=0&&index<STATS_COUNT) ? stats_names[index] : "?";
}

// The player's current value backing a requirement
static int PlayerReqValue(GuildReq r, const int *attrs, const int *skills, const int *stats)
{
    if(r.kind==REQ_ATTR)  return (r.index>=0&&r.index<ATTRIBUTES_COUNT)? attrs[r.index]  : 0;
    if(r.kind==REQ_SKILL) return (r.index>=0&&r.index<SKILL_COUNT)     ? skills[r.index] : 0;
    return (r.index>=0&&r.index<STATS_COUNT) ? stats[r.index] : 0;
}

static bool MeetsGuild(const Guild *g, const int *attrs, const int *skills, const int *stats)
{
    for(int i=0;i<g->reqCount;i++)
        if(PlayerReqValue(g->reqs[i], attrs, skills, stats) < g->reqs[i].minValue) return false;
    return true;
}

// After the NPC roster is resized/regenerated, an NPC leaderIdx may point past the
// new memberCount - pull it back onto a real slot (or hand leadership to you if no
// NPCs are left). Never touches leaderIdx==-1 (you), since that's always valid.
static void ClampGuildLeader(Guild *g)
{
    if(g->leaderIdx>=0 && g->leaderIdx>=g->memberCount)
        g->leaderIdx = (g->memberCount>0) ? 0 : -1;
}

static int GuildTotalMembers(int gi)
{
    if(gi<0||gi>=guildCount) return 0;
    return guilds[gi].memberCount + (gi==joinedGuild ? 1 : 0);
}

// ---- Name pool -------------------------------------------------------------

static char firstNames[MAX_FIRST_NAMES][MAX_GNAME];
static int  firstNameCount = 0;
static char lastNames[MAX_LAST_NAMES][MAX_GNAME];
static int  lastNameCount  = 0;

// Bundled defaults, used to seed FIRST_NAMES_FILE / LAST_NAMES_FILE the first time they don't exist.
static const char *defaultFirstNames[] = {
"Aeric","Bryn","Caelum","Dahlia","Eryndor","Fenwick","Gwyneth","Halric",
"Isolde","Joran","Kaelith","Lysandra","Marek","Nyssa","Orrin","Perrin",
"Quillan","Rowan","Sylas","Thessaly","Ulric","Vesper","Wren","Xanthe",
"Yorick","Zephyra","Alaric","Brielle","Corvin","Delphine","Emrys","Faye",
"Galen","Hesper","Ivorwen","Kestrel","Lucan","Mireille","Nolwen","Osric",
"Alden","Aldenor","Alwyn","Ansel","Arden","Aric","Arlen","Armand","Abel","Adriel","Aeron","Aidan","Alban","Albrecht","Alon","Alton",
"Amias","Andor","Anselm","Aramis","Ariston","Arlo","Armin","Arnor",
"Asa","Ashwin","Atreus","Aurel","Baird","Baldric","Barret","Basil",
"Beckett","Benedict","Berengar","Blaine","Borek","Brennan","Briar","Brogan",
"Byron","Caiden","Callan","Calvin","Camden","Casimir","Castor","Cato",
"Cedran","Cian","Clovis","Conall","Conrad","Corbett","Cormac","Crispin",
"Cyprian","Dacre","Damon","Darian","Darrow","Davin","Declan","Dorianus",
"Drystan","Duncan","Durand","Edmund","Edwin","Elias","Elmar","Elwin",
"Emeric","Endric","Enzo","Erland","Ernest","Ethan","Ewald","Fabian",
"Fendrel","Fergus","Fintan","Fletcher","Gaius","Garin","Gawain","Geoffrey",
"Geralt","Gerard","Gilbert","Godfrey","Grady","Griffith","Hadley","Hamish",
"Harold","Harwin","Heath","Hector","Hugo","Ignatius","Ingram","Isaac",
"Jacoby","Jareth","Jericho","Joachim","Jonas","Julian","Justus","Kaius",
"Karlon","Keegan","Keir","Kenric","Killian","Lachlan","Lance","Lazarus",
"Leander","Leif","Lennox","Leopold","Lloyd","Lowen","Luther","Maddox",
"Malcolm","Marcellus","Marlon","Matthias","Maxim","Milo","Montgomery","Morgan",
"Nash","Nathaniel","Nevan","Niklas","Octavian","Oliver","Orlando","Orson",
"Pascal","Phineas","Quentin","Quintus","Rafferty","Raynor","Reginald","Remus",
"Reynard","Roderick","Roland","Rufus","Samwell","Sebastian","Severin","Sigmund",
"Silvan","Stellan","Sullivan","Talon","Tavian","Tobias","Torin","Trenton",
"Tristanus","Ulf","Uther","Valentin","Viggo","Vincent","Wallace","Walter",
"Warrick","Weston","Wilfred","William","Winston","Wolf","Xavier","Yestin",
"Yvain","Zavian","Zedric","Zenon","Zeph","Zorion","Aimon","Beltran",
"Caesar","Damien","Evren","Flavian","Hador","Isen","Jovian","Kaine","Loric",
"Arwyn","Asher","Aster","Auron","Balin","Bastian","Belric","Beric",
"Bran","Brandon","Brann","Brom","Cadric","Cassian","Cedric","Cillian",
"Corin","Cyrus","Dain","Darian","Daric","Darius","Dorian","Draven",
"Eamon","Edric","Eldric","Elian","Ellis","Elric","Evander","Faelan",
"Falk","Finnian","Florian","Garrick","Gareth","Gavin","Godric","Hadrian",
"Harlan","Henric","Idris","Ilan","Jarek","Jasper","Kaden","Kaelan",
"Kaine","Kellan","Kieran","Larkin","Leoric","Levin","Lorcan","Lucien",
"Magnus","Malric","Mathis","Merrick","Nerian","Niall","Oberon","Orion",
"Oswin","Percival","Ragnar","Rainer","Ronan","Soren","Stellan","Tarin",
"Theron","Tristan","Valen","Varian","Victor","Wystan","Zarek","Zorian",
"Adara","Adeline","Aeliana","Aeris","Aila","Alina","Amaris","Anwen",
"Aria","Arielle","Astrid","Aurora","Belinda","Briala","Brienne","Caelia",
"Cassandra","Celeste","Cerys","Cordelia","Daphne","Elara","Elena","Elise",
"Elyra","Esme","Evelyn","Fiora","Freya","Genevieve","Giselle","Helena",
"Ilyana","Iris","Jessamine","Jocelyn","Kaia","Karina","Larissa","Leona",
"Lilith","Lorelei","Lyra","Maeve","Maris","Melisande","Meridia","Morgana",
"Nadia","Naia","Nerissa","Odette","Olwen","Ophelia","Oriana","Penelope",
"Rhiannon","Rosalind","Rowena","Sabine","Selene","Seraphine","Sigrid","Soraya",
"Sylvia","Talisa","Talia","Thalia","Valeria","Verena","Vivienne","Ysolde",
"Zara","Zinnia","Althea","Bryony","Callista","Danica","Eira","Elowen",
"Faelina","Gwendolyn","Illyria","Jessara","Kalara","Linnea","Mirabel","Nerys"
};

static const char *defaultLastNames[] = {
"Ashford","Blackwood","Cinderfell","Duskbane","Emberlyn","Frostvale",
"Greymoor","Hawthorne","Ironmark","Karsith","Lorekeep","Moonbrook",
"Nightriver","Oakenshield","Ravenscar","Stormwarden","Thornfield",
"Valemont","Wolfsbane","Yarrowdale", "Amberfall","Ashwalker","Ashweaver","Autumnvale","Bearclaw","Bitterbrook",
"Blackbriar","Blackcrest","Blackforge","Blackhill","Blackriver","Blackstone",
"Bloodmoon","Bluebrook","Brightblade","Brightbrook","Brightshield","Bronzebrook",
"Bronzeforge","Brookfield","Caskwood","Cedarbrook","Clearwater","Cloudbreaker",
"Coldridge","Copperhill","Crowhaven","Crystalbrook","Darkbriar","Darkcrest",
"Darkgrove","Dawnhammer","Dawnwatch","Deepforge","Deepwater","Dragonbane",
"Dragoncrest","Dragonfall","Dragonheart","Dragonshield","Driftwood","Duskridge",
"Eaglecrest","Eastbrook","Eastwood","Ebonbrook","Eboncrest","Elderbrook",
"Elderstone","Elmwood","Everbrook","Everfield","Everwood","Falconcrest","Amberstone","Arborvale","Ashgrove","Ashmere","Ashridge","Ashstone","Autumnbrook","Barrowfield",
"Bearridge","Bellwater","Birchfield","Birchgrove","Blackbarrow","Blackhill","Blacklake","Blackmarsh",
"Blackpeak","Blackvale","Blazeforge","Bluefield","Bluehaven","Boarcrest","Boldstone","Boulderhall",
"Brightfield","Brightvale","Brookhaven","Brunnhall","Burningoak","Caskhall","Cedarfield","Cedarridge",
"Cloudfield","Cloudspire","Coldbrook","Coldwater","Copperbrook","Copperfield","Copperridge","Coralridge",
"Crowfield","Crowstone","Crystalfall","Darkfield","Darkhall","Darkmoor","Dawnfield","Dawnspire",
"Deepbrook","Deepstone","Deerfield","Dragonbrook","Dragonspire","Drybrook","Duskhaven","Dustvale",
"Eaglefield","Earthshield","Eastcliff","Eastvale","Ebonfield","Ebongrove","Echowood","Elderfield",
"Elmfield","Emberfield","Emberforge","Everfield","Fairbrook","Fairfield","Falconfield","Fallowbrook",
"Fernfield","Firebrook","Firecliff","Firestone","Flamebrook","Flintbrook","Fogfield","Foresthall",
"Foxbrook","Foxfield","Frostfield","Frosthall","Frostspire","Goldfield","Goldforge","Goldenbrook",
"Goldenhall","Granitebrook","Graniteridge","Grayfield","Grayforge","Greenbrook","Greencrest","Greenstone",
"Griffinfield","Hammerbrook","Hammerstone","Hartfield","Hawkridge","Hazelbrook","Hazelwood","Highforge",
"Hillbrook","Hollowstone","Hornfield","Icefield","Ironfield","Ironpeak","Ivystone","Kingsbrook",
"Kingshall","Kingsridge","Lakefield","Larkfield","Lightfield","Lioncrest","Longbrook","Maplebrook",
"Maplefield","Marshfield","Mistfield","Mistgrove","Moonfield","Moonstone","Mossfield","Nightbrook",
"Nightcrest","Nightgrove","Northfield","Northhall","Oakfield","Oakforge","Oakhall","Oldbrook",
"Oldridge","Pinefield","Pinehall","Quickbrook","Rainfield","Ravenfield","Ravenshade","Redbrook",
"Redforge","Redhall","Riverfield","Riverhall","Rockbrook","Rockhall","Rosefield","Runefield",
"Runehall","Sablebrook","Sablefield","Shadowfield","Shadowhall","Shadowstone","Silverfield","Silverhall",
"Skyfield","Snowfield","Snowhall","Southfield","Springfield","Stagfield","Starfield","Steelfield",
"Stonefield","Stonehall","Stormfield","Stormhall","Summerbrook","Sunfield","Swiftfield","Thornhall",
"Timberbrook","Timberfield","Truefield","Valefield","Westbrook","Westfield","Westhall","Whitefield",
"Whitegrove","Wildfield","Wildhall","Windfield","Windhall","Winterbrook","Winterfield","Wintergrove",
"Wolfbrook","Wolffield","Woodhall","Woodstone","Wyvernfield","Ashthorn","Briarstone","Cinderstone",
"Darkstone","Emberstone","Firebrand","Frostbrand","Goldbrand","Ironbrand","Moonbrand","Nightbrand",
"Falconridge","Fernbrook","Fireforge","Fireheart","Flintstone","Fogbrook",
"Foxglove","Foxhill","Frostbrook","Frostforge","Frostheart","Frostmark",
"Goldbrook","Goldcrest","Goldleaf","Goldridge","Goldenfield","Granitehall",
"Graybrook","Graystone","Greenfield","Greenhill","Greenthorn","Griffinbane",
"Griffincrest","Griffinhall","Hammerfall","Hartwood","Highbrook","Highcliff",
"Highfield","Hillcrest","Hillford","Hollowbrook","Hollowfield","Hornvale",
"Icebrook","Iceforge","Ironbrook","Ironcliff","Ironforge","Ironhall",
"Ironridge","Kingsfield","Kingsford","Kingsley","Kingsmark","Lakewood",
"Larkspur","Lightbringer","Lightbrook","Longfield","Longridge","Marblehall",
"Meadowbrook","Mistbrook","Mistvale","Mooncrest","Moonfall","Moonridge",
"Mossbrook","Nightbloom","Nightfall","Nightshade","Northbrook","Northcliff",
"Oakbrook","Oakridge","Oldfield","Pinebrook","Pinecrest","Pinegrove",
"Quickwater","Rainbrook","Ravenbrook","Ravencrest","Redbrook","Redcliff",
"Redfield","Redstone","Riverbend","Riverstone","Rockfield","Rosebrook",
"Rowanfield","Runeblade","Runeforge","Runestone","Shadowbane","Shadowbrook",
"Shadowcrest","Shadowfen","Shadowforge","Shadowgrove","Silverbrook","Silvercrest",
"Silverforge","Silverleaf","Silverstone","Skylark","Snowbrook","Snowfall",
"Southbrook","Springbrook","Stagcrest","Starbrook","Starcrest","Steelbrook",
"Steelhammer","Stonebridge","Stonebrook","Stonecliff","Stonefield","Stoneforge",
"Stoneridge","Stormbrook","Stormcliff","Stormcrest","Stormforge","Summerfield",
"Sunbrook","Suncrest","Swiftbrook","Thornbrook","Timberfall","Truebrook",
"Valebrook","Valewood","Whitebrook","Whitecliff","Whitefield","Whiteforge",
"Whitehall","Wildbrook","Wildgrove","Windbrook","Windcrest","Windrider",
"Winterbrook","Wintercrest","Winterfield","Winterhall","Woodbrook","Woodfield",
"Woodridge","Wyverncrest"
};

// Loads one name per line from `path` into `arr` (skips blank lines and '#' comments).
// Returns how many were loaded; 0 if the file doesn't exist or is empty.
static int LoadNameList(const char *path, char arr[][MAX_GNAME], int maxCount)
{
    FILE *f = fopen(path,"r");
    if(!f) return 0;
    int n=0;
    char line[256];
    while(n<maxCount && fgets(line,sizeof(line),f)){
        int len=(int)strlen(line);
        while(len>0 && (line[len-1]=='\n'||line[len-1]=='\r'||line[len-1]==' '||line[len-1]=='\t'))
            line[--len]='\0';
        char *p=line; while(*p==' '||*p=='\t') p++;
        if(*p=='\0'||*p=='#') continue;
        strncpy(arr[n], p, MAX_GNAME-1);
        arr[n][MAX_GNAME-1]='\0';
        n++;
    }
    fclose(f);
    return n;
}

// Writes the bundled defaults to `path` so the user has a file to edit/replace.
static void SeedNameListFile(const char *path, const char *label, const char **defaults, int count)
{
    FILE *f = fopen(path,"w");
    if(!f) return;
    fprintf(f,"# Aetheria %s\n", label);
    fprintf(f,"# One name per line. Lines starting with # are ignored.\n#\n");
    for(int i=0;i<count;i++) fprintf(f,"%s\n", defaults[i]);
    fclose(f);
}

void EnsureNamePool(void)
{
    // Auto-clean both name lists (drop duplicate lines, sort alphabetically) before loading them.
    DedupeSortTextFile(FIRST_NAMES_FILE);
    DedupeSortTextFile(LAST_NAMES_FILE);

    firstNameCount = LoadNameList(FIRST_NAMES_FILE, firstNames, MAX_FIRST_NAMES);
    if(firstNameCount==0){
        int nDef=(int)(sizeof(defaultFirstNames)/sizeof(defaultFirstNames[0]));
        SeedNameListFile(FIRST_NAMES_FILE, "First Names", defaultFirstNames, nDef);
        if(nDef>MAX_FIRST_NAMES) nDef=MAX_FIRST_NAMES;
        for(int i=0;i<nDef;i++){ strncpy(firstNames[i],defaultFirstNames[i],MAX_GNAME-1); firstNames[i][MAX_GNAME-1]='\0'; }
        firstNameCount=nDef;
    }

    lastNameCount = LoadNameList(LAST_NAMES_FILE, lastNames, MAX_LAST_NAMES);
    if(lastNameCount==0){
        int nDef=(int)(sizeof(defaultLastNames)/sizeof(defaultLastNames[0]));
        SeedNameListFile(LAST_NAMES_FILE, "Last Names", defaultLastNames, nDef);
        if(nDef>MAX_LAST_NAMES) nDef=MAX_LAST_NAMES;
        for(int i=0;i<nDef;i++){ strncpy(lastNames[i],defaultLastNames[i],MAX_GNAME-1); lastNames[i][MAX_GNAME-1]='\0'; }
        lastNameCount=nDef;
    }
}

// Draw a random, duplicate-free NPC roster for a guild (partial Fisher-Yates).
// desiredCount<0 picks a random size (leaving room for the founder to join later);
// desiredCount>=0 uses that exact size, up to MAX_GUILD_MEMBERS (50 = completely full).
static bool GuildHasMemberName(const Guild *g, const char *name)
{
    for(int i=0;i<g->memberCount;i++)
        if(strcmp(g->members[i],name)==0) return true;
    return false;
}

static void PopulateRoster(Guild *g, int desiredCount)
{
    g->memberCount = 0;
    if(firstNameCount<=0 || lastNameCount<=0) return;

    int count;
    if(desiredCount<0){
        int maxNpc = MAX_GUILD_MEMBERS - 1;             // leave a slot for the player
        count = (maxNpc<=8) ? maxNpc : (8 + rand()%(maxNpc-7));
    } else {
        count = desiredCount;
        if(count > MAX_GUILD_MEMBERS) count = MAX_GUILD_MEMBERS;
        if(count < 0) count = 0;
    }

    // Draw each member as an independent random first+last combo (retrying on
    // an in-guild duplicate) instead of sampling from a size-capped pre-built
    // pool — that used to bias heavily toward the first few first names.
    for(int n=0;n<count;n++){
        char candidate[MAX_GNAME];
        int tries=0;
        do {
            const char *fn = firstNames[rand()%firstNameCount];
            const char *ln = lastNames [rand()%lastNameCount];
            snprintf(candidate, MAX_GNAME, "%s %s", fn, ln);
            tries++;
        } while(tries<25 && GuildHasMemberName(g,candidate));
        strncpy(g->members[g->memberCount], candidate, MAX_GNAME-1);
        g->members[g->memberCount][MAX_GNAME-1]='\0';
        g->memberCount++;
    }
}

// ---- Persistence -----------------------------------------------------------

void GuildSave(void)
{
    FILE *f = fopen(GUILD_SAVE_FILE,"w");
    if(!f){ ShowNotif("ERROR: Could not save guilds!"); return; }
    fprintf(f,"# Aetheria Guild Register\nJOINED %d\n\n", joinedGuild);
    for(int i=0;i<guildCount;i++){
        Guild *g=&guilds[i];
        fprintf(f,"GUILD\nname=%s\n", g->name);
        fprintf(f,"leader=%d\n", g->leaderIdx);
        for(int r=0;r<g->reqCount;r++)
            fprintf(f,"req=%d %d %d\n", (int)g->reqs[r].kind, g->reqs[r].index, g->reqs[r].minValue);
        for(int m=0;m<g->memberCount;m++)
            fprintf(f,"member=%s\n", g->members[m]);
        fprintf(f,"---\n");
    }
    fclose(f);
}

void GuildLoad(void)
{
    FILE *f = fopen(GUILD_SAVE_FILE,"r");
    if(!f) return;
    char line[256];
    Guild *cur=NULL;
    guildCount=0; joinedGuild=-1;
    while(fgets(line,sizeof(line),f)){
        int n=(int)strlen(line);
        while(n>0 && (line[n-1]=='\n'||line[n-1]=='\r')) line[--n]='\0';
        if(line[0]=='#') continue;
        if(strncmp(line,"JOINED ",7)==0){ joinedGuild=atoi(line+7); }
        else if(strcmp(line,"GUILD")==0){
            if(guildCount<MAX_GUILDS){ cur=&guilds[guildCount++]; memset(cur,0,sizeof(Guild)); }
            else cur=NULL;
        }
        else if(cur && strncmp(line,"name=",5)==0){
            strncpy(cur->name, line+5, MAX_GNAME-1); cur->name[MAX_GNAME-1]='\0';
        }
        else if(cur && strncmp(line,"leader=",7)==0){
            cur->leaderIdx=atoi(line+7);
        }
        else if(cur && strncmp(line,"req=",4)==0 && cur->reqCount<MAX_GUILD_REQS){
            int k=0,ix=0,mv=0;
            if(sscanf(line+4,"%d %d %d",&k,&ix,&mv)==3){
                cur->reqs[cur->reqCount].kind=(ReqKind)k;
                cur->reqs[cur->reqCount].index=ix;
                cur->reqs[cur->reqCount].minValue=mv;
                cur->reqCount++;
            }
        }
        else if(cur && strncmp(line,"member=",7)==0 && cur->memberCount<MAX_GUILD_MEMBERS){
            strncpy(cur->members[cur->memberCount], line+7, MAX_GNAME-1);
            cur->members[cur->memberCount][MAX_GNAME-1]='\0';
            cur->memberCount++;
        }
        else if(strcmp(line,"---")==0){ if(cur) ClampGuildLeader(cur); cur=NULL; }
    }
    if(joinedGuild<-1||joinedGuild>=guildCount) joinedGuild=-1;
    fclose(f);
}

// ---- Create a guild from the working buffers -------------------------------

static bool GuildNameExists(const char *name)
{
    for(int i=0;i<guildCount;i++)
        if(strcmp(guilds[i].name,name)==0) return true;
    return false;
}

// Disbands a guild outright (distinct from a member simply leaving it).
static void DeleteGuild(int gi)
{
    if(gi<0||gi>=guildCount) return;
    for(int i=gi;i<guildCount-1;i++) guilds[i]=guilds[i+1];
    guildCount--;
    if(joinedGuild==gi)      joinedGuild=-1;
    else if(joinedGuild>gi)  joinedGuild--;
    GuildSave();
}

static void ResetCreateBuffers(void)
{
    memset(tbGuildName.buf,0,sizeof(tbGuildName.buf));
    tbGuildName.active=false;
    for(int i=0;i<ATTRIBUTES_COUNT;i++) newReqAttr[i]=0;
    for(int i=0;i<SKILL_COUNT;i++)      newReqSkill[i]=0;
    for(int i=0;i<STATS_COUNT;i++)      newReqStat[i]=0;
    createCat=0; createScroll=0.f;
    newMemberCount=-1;
}

static bool CommitNewGuild(void)
{
    if(strlen(tbGuildName.buf)==0){ ShowNotif("Enter a guild name first."); return false; }
    if(guildCount>=MAX_GUILDS)    { ShowNotif("Guild register is full.");   return false; }
    if(GuildNameExists(tbGuildName.buf)){ ShowNotif("A guild with that name exists."); return false; }

    Guild *g=&guilds[guildCount];
    memset(g,0,sizeof(Guild));
    safe_strncpy(g->name, tbGuildName.buf, MAX_GNAME);

    for(int i=0;i<ATTRIBUTES_COUNT && g->reqCount<MAX_GUILD_REQS;i++)
        if(newReqAttr[i]>0){ g->reqs[g->reqCount++]=(GuildReq){REQ_ATTR ,i,newReqAttr[i]}; }
    for(int i=0;i<SKILL_COUNT && g->reqCount<MAX_GUILD_REQS;i++)
        if(newReqSkill[i]>0){ g->reqs[g->reqCount++]=(GuildReq){REQ_SKILL,i,newReqSkill[i]}; }
    for(int i=0;i<STATS_COUNT && g->reqCount<MAX_GUILD_REQS;i++)
        if(newReqStat[i]>0){ g->reqs[g->reqCount++]=(GuildReq){REQ_STAT ,i,newReqStat[i]}; }

    PopulateRoster(g, newMemberCount);
    // Founding a guild doesn't make you its leader by default - an existing member does,
    // unless there are no NPC members at all (then you're the only option).
    g->leaderIdx = (g->memberCount>0) ? 0 : -1;
    guildCount++;
    GuildSave();
    ShowNotif(TextFormat("Guild '%s' founded with %d members.", g->name, g->memberCount));
    return true;
}

// ---- Shared drawing bits ---------------------------------------------------

// Top-right back button; returns true when clicked
static bool GuildBackButton(int cX, int cY)
{
    Rectangle r={(float)(cX+G_W-92),(float)(cY-2),88.f,26.f};
    return SmallBtn(r,"< Back",C_PANEL3,C_MUTED);
}

// ---- View 0: three-option menu --------------------------------------------

static void DrawGuildMenu(int cX, int cY)
{
    DrawText("GUILD",cX,cY,22,C_GOLD_L);
    HDivider(cX,cY+28,G_W);

    struct { const char *title; const char *sub; int view; } cards[3];

    const char *guildName = (joinedGuild>=0) ? guilds[joinedGuild].name : "Your Guild";
    cards[0].title = TextFormat("%s  -  Menu", guildName);
    cards[0].sub   = (joinedGuild>=0) ? "View your roster & guild statistics"
                                      : "You haven't joined a guild yet";
    cards[0].view  = 1;
    cards[1].title = "Join / Switch Guild";
    cards[1].sub   = "Check prerequisites, then enlist or transfer";
    cards[1].view  = 2;
    cards[2].title = "Create Guild";
    cards[2].sub   = "Define requirements and found a new order";
    cards[2].view  = 3;

    int cy=cY+46, ch=116, gap=14;
    for(int i=0;i<3;i++){
        Rectangle r={(float)cX,(float)cy,(float)G_W,(float)ch};
        bool hov=CheckCollisionPointRec(GetMousePosition(),r);
        Color bg = hov?C_PANEL3:C_PANEL2;
        DrawRectangleRounded(r,0.05f,8,bg);
        DrawRectangleRoundedLinesEx(r,0.05f,8,1.5f,hov?C_GOLD:C_BORDER);
        DrawRectangle((int)r.x,(int)r.y+10,4,ch-20,C_GOLD);   // left accent bar

        DrawText(cards[i].title, cX+26, cy+30, 24, hov?C_GOLD_L:C_TEXT);
        DrawText(cards[i].sub,   cX+26, cy+68, 14, C_MUTED);
        DrawText(">", cX+G_W-34, cy+ch/2-16, 30, hov?C_GOLD_L:C_MUTED);

        if(hov&&IsMouseButtonPressed(MOUSE_LEFT_BUTTON)){
            guild_view=cards[i].view;
            guildScroll=joinScroll=createScroll=0.f;
            deleteConfirmGuild=-1;
        }
        cy+=ch+gap;
    }
}

// ---- Requirements modal: tabular breakdown for one guild -------------------

void DrawGuildReqModal(const int *attrs, const int *skills, const int *stats)
{
    if(guild_req_modal<0||guild_req_modal>=guildCount) return;
    Guild *g=&guilds[guild_req_modal];

    DrawRectangle(0,0,900,600,Q_OVERLAY);

    int mw=560, mh=440;
    int mx=(900-mw)/2, my=(600-mh)/2;
    Rectangle modal={(float)mx,(float)my,(float)mw,(float)mh};
    DrawRectangleRounded(modal,0.06f,8,(Color){15,11,32,255});
    DrawRectangleRoundedLinesEx(modal,0.06f,8,1.5f,Q_BORDER2);

    DrawRectangleRounded((Rectangle){(float)mx,(float)my,(float)mw,46},0.06f,6,(Color){25,18,50,255});
    DrawText(TextFormat("Requirements  -  %s",g->name),mx+18,my+13,18,Q_GPALE);

    int colName=mx+18, colReq=mx+18+300, colYou=mx+18+420, colW=90;
    int hy=my+58;
    DrawText("REQUIREMENT",colName,hy,12,C_MUTED);
    DrawText("REQUIRED",   colReq, hy,12,C_MUTED);
    DrawText("YOU",        colYou, hy,12,C_MUTED);
    HDivider(mx+18,hy+18,mw-36);

    int listY=hy+26, listH=(my+mh-58)-listY;
    int rowH=32;

    if(g->reqCount==0){
        DrawText("Open charter - no requirements to join.",colName,listY+8,14,C_GREEN_L);
    } else {
        if(CheckCollisionPointRec(GetMousePosition(),(Rectangle){(float)mx,(float)listY,(float)mw,(float)listH})){
            reqModalScroll -= GetMouseWheelMove()*rowH;
            float maxS=fmaxf(0.f,(float)(g->reqCount*rowH-listH));
            if(reqModalScroll<0.f) reqModalScroll=0.f;
            if(reqModalScroll>maxS) reqModalScroll=maxS;
        }

        BeginScissorMode(mx,listY,mw,listH);
        int ry=listY-(int)reqModalScroll;
        for(int i=0;i<g->reqCount;i++){
            if(ry+rowH>=listY&&ry<=listY+listH){
                const char *nm=ReqItemName(g->reqs[i].kind,g->reqs[i].index);
                int mvv=g->reqs[i].minValue;
                int pv=PlayerReqValue(g->reqs[i],attrs,skills,stats);
                bool met=(pv>=mvv);

                Rectangle rr={(float)colName,(float)ry,(float)(mw-36),(float)(rowH-6)};
                DrawRectangleRounded(rr,0.08f,6,i%2?C_PANEL:C_PANEL2);
                DrawRectangle((int)rr.x,(int)rr.y,3,(int)rr.height,met?C_GREEN:C_RED);

                DrawText(nm,colName+12,ry+7,14,C_TEXT);
                char rv[16]; snprintf(rv,sizeof(rv),"%d",mvv);
                DrawText(rv,colReq+(colW-MeasureText(rv,14))/2,ry+7,14,C_MUTED);
                char yv[16]; snprintf(yv,sizeof(yv),"%d",pv);
                DrawText(yv,colYou+(colW-MeasureText(yv,14))/2,ry+7,14,met?C_GREEN_L:C_RED_L);
            }
            ry+=rowH;
        }
        EndScissorMode();
    }

    if(Btn((Rectangle){(float)(mx+mw/2-90),(float)(my+mh-46),180.f,36.f},"Close",C_PANEL3,C_TEXT))
        guild_req_modal=-1;
}

// ---- View 1: your guild ----------------------------------------------------

static void DrawGuildYours(int cX, int cY, const int *attrs, const int *skills, const int *stats)
{
    (void)attrs; (void)skills; (void)stats;
    const char *heading = (joinedGuild>=0) ? guilds[joinedGuild].name : "Your Guild";
    DrawText(heading,cX,cY,22,C_GOLD_L);
    if(GuildBackButton(cX,cY)) guild_view=0;
    HDivider(cX,cY+28,G_W);

    if(joinedGuild<0){
        Rectangle p={(float)cX,(float)(cY+60),(float)G_W,300.f};
        Panel(p,C_PANEL2,C_BORDER);
        const char *m1="You are not a member of any guild.";
        const char *m2="Join an existing guild or found your own.";
        DrawText(m1,cX+(G_W-MeasureText(m1,18))/2,cY+150,18,C_TEXT);
        DrawText(m2,cX+(G_W-MeasureText(m2,14))/2,cY+180,14,C_MUTED);
        if(Btn((Rectangle){(float)(cX+G_W/2-110),(float)(cY+220),220.f,42.f},
               "Browse Guilds",C_GOLD,C_BG)){ guild_view=2; joinScroll=0.f; deleteConfirmGuild=-1; }
        return;
    }

    Guild *g=&guilds[joinedGuild];
    int total=GuildTotalMembers(joinedGuild);

    int y=cY+44;
    DrawText(g->name,cX,y,28,C_GOLD_L);
    char mb[32]; snprintf(mb,sizeof(mb),"%d / %d MEMBERS",total,MAX_GUILD_MEMBERS);
    Badge(cX+MeasureText(g->name,28)+18,y+8,mb,C_PANEL3,C_GREEN_L);
    y+=44;

    DrawText("REQUIREMENTS",cX,y,12,C_MUTED); y+=18;
    if(g->reqCount==0){
        DrawText("Open charter - no requirements to join.",cX,y,14,C_GREEN_L); y+=26;
    } else {
        Rectangle reqBtn={(float)cX,(float)y,176.f,24.f};
        if(SmallBtn(reqBtn,"Show Requirements",C_PANEL3,C_MUTED)){ guild_req_modal=joinedGuild; reqModalScroll=0.f; }
        y+=34;
    }

    DrawText("MEMBER COUNT",cX,y,12,C_MUTED); y+=18;
    {
        Rectangle rMinus={(float)cX,(float)y,32.f,26.f};
        Rectangle rPlus ={(float)(cX+38),(float)y,32.f,26.f};
        bool rShift=(IsKeyDown(KEY_LEFT_SHIFT)||IsKeyDown(KEY_RIGHT_SHIFT));
        int rStep=rShift?10:1;

        char rcs[24]; snprintf(rcs,sizeof(rcs),"%d / %d",total,MAX_GUILD_MEMBERS);
        DrawText(rcs,cX+80,y+5,16,C_TEXT);

        bool atMin=(g->memberCount<=0);
        bool atMax=(g->memberCount>=MAX_GUILD_MEMBERS-1);   // 49 NPCs + you = 50, the roster cap

        if(SmallBtn(rMinus,"-",atMin?C_PANEL2:C_PANEL3,atMin?C_MUTED:C_TEXT) && !atMin){
            int nc=g->memberCount-rStep; if(nc<0)nc=0;
            PopulateRoster(g,nc); ClampGuildLeader(g); GuildSave();
        }
        if(SmallBtn(rPlus,"+",atMax?C_PANEL2:C_PANEL3,atMax?C_MUTED:C_TEXT) && !atMax){
            int nc=g->memberCount+rStep; if(nc>MAX_GUILD_MEMBERS-1)nc=MAX_GUILD_MEMBERS-1;
            PopulateRoster(g,nc); ClampGuildLeader(g); GuildSave();
        }
    }
    y+=34;

    DrawText("ROSTER",cX,y,12,C_MUTED); y+=18;

    int listY=y, listH=G_BOTTOM-52-listY;   // leave room for Leave button
    int rowH=30;
    int totalRows=g->memberCount+1;          // +1 for the player row

    if(CheckCollisionPointRec(GetMousePosition(),(Rectangle){(float)cX,(float)listY,(float)G_W,(float)listH})){
        guildScroll -= GetMouseWheelMove()*rowH;
        float maxS=fmaxf(0.f,(float)(totalRows*rowH-listH));
        if(guildScroll<0.f) guildScroll=0.f;
        if(guildScroll>maxS) guildScroll=maxS;
    }

    BeginScissorMode(cX,listY,G_W,listH);
    int ry=listY-(int)guildScroll;

    // Player row (always first, highlighted)
    {
        Rectangle rr={(float)cX,(float)ry,(float)G_W,(float)(rowH-4)};
        DrawRectangleRounded(rr,0.15f,6,(Color){30,36,26,255});
        DrawRectangleRoundedLinesEx(rr,0.15f,6,1.f,C_GOLD_D);
        DrawText("You  (S_Lord)",cX+12,ry+5,15,C_GOLD_L);
        if(g->leaderIdx==-1){
            Badge(cX+G_W-190,ry+3,"LEADER",C_GOLD_D,C_GOLD_L);
        } else {
            Rectangle mkR={(float)(cX+G_W-190),(float)(ry+3),110.f,20.f};
            if(SmallBtn(mkR,"Make Leader",C_PANEL3,C_MUTED)){ g->leaderIdx=-1; GuildSave(); }
        }
    }
    ry+=rowH;

    for(int i=0;i<g->memberCount;i++){
        if(ry+rowH<listY||ry>listY+listH){ ry+=rowH; continue; }
        Rectangle rr={(float)cX,(float)ry,(float)G_W,(float)(rowH-4)};
        DrawRectangleRounded(rr,0.12f,6,i%2? C_PANEL : C_PANEL2);
        DrawText(g->members[i],cX+12,ry+5,15,C_TEXT);
        if(g->leaderIdx==i){
            Badge(cX+G_W-190,ry+3,"LEADER",C_GOLD_D,C_GOLD_L);
        } else {
            Rectangle mkR={(float)(cX+G_W-190),(float)(ry+3),110.f,20.f};
            if(SmallBtn(mkR,"Make Leader",C_PANEL3,C_MUTED)){ g->leaderIdx=i; GuildSave(); }
        }
        char num[16]; snprintf(num,sizeof(num),"#%d",i+2);
        DrawText(num,cX+G_W-MeasureText(num,13)-14,ry+7,13,C_MUTED);
        ry+=rowH;
    }
    EndScissorMode();

    if(Btn((Rectangle){(float)cX,(float)(G_BOTTOM-42),200.f,38.f},"Leave Guild",C_RED,C_TEXT)){
        ShowNotif(TextFormat("You left %s.",g->name));
        joinedGuild=-1; guildScroll=0.f;
        GuildSave();
    }
}

// ---- View 2: join / switch -------------------------------------------------

static void DrawGuildJoin(int cX, int cY, const int *attrs, const int *skills, const int *stats)
{
    DrawText("Join / Switch Guild",cX,cY,22,C_GOLD_L);
    if(GuildBackButton(cX,cY)){ guild_view=0; deleteConfirmGuild=-1; }
    HDivider(cX,cY+28,G_W);

    if(guildCount==0){
        Rectangle p={(float)cX,(float)(cY+60),(float)G_W,300.f};
        Panel(p,C_PANEL2,C_BORDER);
        const char *m1="No guilds have been founded yet.";
        DrawText(m1,cX+(G_W-MeasureText(m1,18))/2,cY+170,18,C_TEXT);
        if(Btn((Rectangle){(float)(cX+G_W/2-110),(float)(cY+210),220.f,42.f},
               "Create a Guild",C_GOLD,C_BG)){ guild_view=3; ResetCreateBuffers(); }
        return;
    }

    int listY=cY+44, listH=G_BOTTOM-listY;
    int cardH=104, gap=12;
    int totalH=guildCount*(cardH+gap);

    if(CheckCollisionPointRec(GetMousePosition(),(Rectangle){(float)cX,(float)listY,(float)G_W,(float)listH})){
        joinScroll -= GetMouseWheelMove()*46.f;
        float maxS=fmaxf(0.f,(float)(totalH-listH));
        if(joinScroll<0.f) joinScroll=0.f;
        if(joinScroll>maxS) joinScroll=maxS;
    }

    BeginScissorMode(cX-3,listY,G_W+6,listH+2);   // pad 3px each side so card outlines aren't clipped
    int cy=listY-(int)joinScroll;

    for(int i=0;i<guildCount;i++){
        if(cy+cardH<listY||cy>listY+listH){ cy+=cardH+gap; continue; }
        Guild *g=&guilds[i];
        bool isJoined=(i==joinedGuild);
        bool meets=MeetsGuild(g,attrs,skills,stats);
        int total=GuildTotalMembers(i);
        bool full=(total>=MAX_GUILD_MEMBERS);

        Rectangle r={(float)cX,(float)cy,(float)G_W,(float)cardH};
        Color bd=isJoined?C_GOLD:meets?C_GREEN:C_BORDER;
        DrawRectangleRounded(r,0.05f,8,C_PANEL2);
        DrawRectangleRoundedLinesEx(r,0.05f,8,1.5f,bd);

        // delete-guild mini option, top-right corner — needs an explicit confirm
        if(deleteConfirmGuild==i){
            const char *warn="Delete?";
            DrawText(warn,cX+G_W-64-MeasureText(warn,12)-8,cy+9,12,C_RED_L);
            Rectangle yesR={(float)(cX+G_W-64),(float)(cy+6),28,22};
            Rectangle noR ={(float)(cX+G_W-32),(float)(cy+6),28,22};
            if(SmallBtn(yesR,"Yes",C_RED,C_TEXT)){
                ShowNotif(TextFormat("'%s' has been disbanded.",g->name));
                DeleteGuild(i);
                deleteConfirmGuild=-1;
                break;   // guild array just shifted — stop drawing this frame's list
            }
            if(SmallBtn(noR,"No",C_PANEL3,C_TEXT)) deleteConfirmGuild=-1;
        } else {
            Rectangle delIcon={(float)(cX+G_W-28),(float)(cy+6),22,22};
            bool delHov=CheckCollisionPointRec(GetMousePosition(),delIcon);
            DrawRectangleRounded(delIcon,0.3f,4,delHov?(Color){60,20,20,255}:C_PANEL2);
            DrawXMark(delIcon,1.5f,C_RED);
            if(delHov&&IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) deleteConfirmGuild=i;
        }

        DrawText(g->name,cX+16,cy+12,20,isJoined?C_GOLD_L:C_TEXT);
        char mb[32]; snprintf(mb,sizeof(mb),"%d/%d",total,MAX_GUILD_MEMBERS);
        Badge(cX+16+MeasureText(g->name,20)+12,cy+15,mb,C_PANEL3,full?C_RED_L:C_MUTED);

        // requirements — hidden by default, shown in a table modal on request
        if(g->reqCount==0){
            DrawText("No requirements - open to all.",cX+16,cy+48,13,C_GREEN_L);
        } else {
            Rectangle reqBtn={(float)(cX+16),(float)(cy+44),176.f,24.f};
            if(SmallBtn(reqBtn,"Show Requirements",C_PANEL3,C_MUTED)){ guild_req_modal=i; reqModalScroll=0.f; }
        }

        // action button
        Rectangle bR={(float)(cX+G_W-136),(float)(cy+cardH-48),120.f,36.f};
        if(isJoined){
            DrawRectangleRounded(bR,0.2f,8,(Color){20,45,24,255});
            DrawRectangleRoundedLinesEx(bR,0.2f,8,1.5f,C_GREEN);
            const char *t="Joined";
            DrawText(t,(int)(bR.x+(bR.width-MeasureText(t,15))/2),(int)(bR.y+10),15,C_GREEN_L);
        } else if(full){
            DrawRectangleRounded(bR,0.2f,8,C_PANEL3);
            const char *t="Full";
            DrawText(t,(int)(bR.x+(bR.width-MeasureText(t,15))/2),(int)(bR.y+10),15,C_MUTED);
        } else if(!meets){
            DrawRectangleRounded(bR,0.2f,8,(Color){40,16,16,255});
            DrawRectangleRoundedLinesEx(bR,0.2f,8,1.f,C_RED);
            const char *t="Locked";
            DrawText(t,(int)(bR.x+(bR.width-MeasureText(t,15))/2),(int)(bR.y+10),15,C_RED_L);
        } else {
            const char *t=(joinedGuild>=0)?"Switch":"Join";
            if(Btn(bR,t,C_GOLD,C_BG)){
                const char *prev=(joinedGuild>=0)?guilds[joinedGuild].name:NULL;
                joinedGuild=i;
                GuildSave();
                if(prev) ShowNotif(TextFormat("Transferred from %s to %s.",prev,g->name));
                else     ShowNotif(TextFormat("You joined %s!",g->name));
            }
        }
        cy+=cardH+gap;
    }
    EndScissorMode();
}

// ---- View 3: create --------------------------------------------------------

static void DrawGuildCreate(int cX, int cY, const int *attrs, const int *skills, const int *stats)
{
    DrawText("Create Guild",cX,cY,22,C_GOLD_L);
    if(GuildBackButton(cX,cY)){ guild_view=0; }
    HDivider(cX,cY+28,G_W);

    int y=cY+44;

    // Guild name
    DrawText("GUILD NAME",cX,y,12,C_MUTED); y+=18;
    Rectangle nameR={(float)cX,(float)y,(float)G_W,38.f};
    TBoxTick(&tbGuildName,nameR);
    TBoxDraw(&tbGuildName,nameR,"e.g.  Emberguard,  The Silver Pact ...");
    y+=52;

    // Member count — leave at "Random" or pick an exact NPC roster size (50 = completely full, unjoinable)
    DrawText("MEMBER COUNT",cX,y,12,C_MUTED); y+=18;
    {
        Rectangle mMinus={(float)cX,(float)y,32.f,26.f};
        Rectangle mPlus ={(float)(cX+38),(float)y,32.f,26.f};
        Rectangle mRand ={(float)(cX+G_W-110),(float)y,110.f,26.f};
        bool mShift=(IsKeyDown(KEY_LEFT_SHIFT)||IsKeyDown(KEY_RIGHT_SHIFT));
        int mStep=mShift?10:1;

        if(SmallBtn(mMinus,"-",C_PANEL3,C_TEXT)){
            int cur=(newMemberCount<0)?0:newMemberCount;
            cur-=mStep; if(cur<0)cur=0;
            newMemberCount=cur;
        }
        if(SmallBtn(mPlus,"+",C_PANEL3,C_TEXT)){
            int cur=(newMemberCount<0)?0:newMemberCount;
            cur+=mStep; if(cur>MAX_GUILD_MEMBERS)cur=MAX_GUILD_MEMBERS;
            newMemberCount=cur;
        }
        char mcs[24];
        if(newMemberCount<0) snprintf(mcs,sizeof(mcs),"Random");
        else                 snprintf(mcs,sizeof(mcs),"%d / %d",newMemberCount,MAX_GUILD_MEMBERS);
        DrawText(mcs,cX+80,y+5,16,newMemberCount<0?C_MUTED:C_GOLD_L);

        if(SmallBtn(mRand,"Random",newMemberCount<0?C_GOLD_D:C_PANEL3,newMemberCount<0?C_GOLD_L:C_MUTED))
            newMemberCount=-1;
    }
    y+=34;

    // Category selector
    int pillW=(G_W-16)/3;
    for(int c=0;c<3;c++){
        Rectangle pr={(float)(cX+c*(pillW+8)),(float)y,(float)pillW,30.f};
        bool active=(createCat==c);
        bool hov=CheckCollisionPointRec(GetMousePosition(),pr);
        DrawRectangleRounded(pr,0.25f,8,active?C_PANEL3:hov?C_PANEL2:C_PANEL);
        DrawRectangleRoundedLinesEx(pr,0.25f,8,1.f,active?C_GOLD:C_BORDER);
        const char *t=ReqCatName(c);
        DrawText(t,(int)(pr.x+(pr.width-MeasureText(t,14))/2),(int)(pr.y+8),14,active?C_GOLD_L:C_MUTED);
        if(hov&&IsMouseButtonPressed(MOUSE_LEFT_BUTTON)){ createCat=c; createScroll=0.f; }
    }
    y+=40;

    // Count how many requirements are currently set (across all categories)
    int reqSet=0;
    for(int i=0;i<ATTRIBUTES_COUNT;i++) if(newReqAttr[i]>0)  reqSet++;
    for(int i=0;i<SKILL_COUNT;i++)      if(newReqSkill[i]>0) reqSet++;
    for(int i=0;i<STATS_COUNT;i++)      if(newReqStat[i]>0)  reqSet++;

    // Attributes: +1 / +10, capped at 100. Stats: +1 / +10, capped at 1000. Skills: +10 / +100, capped at 1000.
    int baseStep  = (createCat==1)?10:1;
    int shiftStep = baseStep*10;
    int maxVal    = (createCat==0)?100:1000;

    DrawText(TextFormat("Requirements set: %d",reqSet),cX,y,13,reqSet>0?C_GREEN_L:C_MUTED);
    char hint[96]; snprintf(hint,sizeof(hint),"Set a minimum > 0 to require it. Hold SHIFT for +%d.",shiftStep);
    DrawText(hint, cX+G_W-MeasureText(hint,12), y+1, 12, C_MUTED);
    y+=22;

    // Scrollable requirement list for the active category
    int listY=y, listH=G_BOTTOM-52-listY;   // leave room for Create button
    int rowH=36, count=ReqCatCount(createCat);
    bool shiftHeld=(IsKeyDown(KEY_LEFT_SHIFT)||IsKeyDown(KEY_RIGHT_SHIFT));
    int step=shiftHeld?shiftStep:baseStep;

    if(CheckCollisionPointRec(GetMousePosition(),(Rectangle){(float)cX,(float)listY,(float)G_W,(float)listH})){
        createScroll -= GetMouseWheelMove()*rowH;
        float maxS=fmaxf(0.f,(float)(count*rowH-listH));
        if(createScroll<0.f) createScroll=0.f;
        if(createScroll>maxS) createScroll=maxS;
    }

    BeginScissorMode(cX,listY,G_W,listH);
    int ry=listY-(int)createScroll;
    for(int i=0;i<count;i++){
        if(ry+rowH<listY||ry>listY+listH){ ry+=rowH; continue; }

        int *slot; const char *nm; int youVal;
        if(createCat==0){ slot=&newReqAttr[i];  nm=attributes_names[i]; youVal=attrs[i];  }
        else if(createCat==1){ slot=&newReqSkill[i]; nm=skill_names[i]; youVal=skills[i]; }
        else { slot=&newReqStat[i]; nm=stats_names[i]; youVal=stats[i]; }

        bool req=(*slot>0);
        Rectangle rr={(float)cX,(float)ry,(float)G_W,(float)(rowH-6)};
        DrawRectangleRounded(rr,0.10f,6,req?(Color){30,30,20,255}:(i%2?C_PANEL:C_PANEL2));
        DrawRectangleRoundedLinesEx(rr,0.10f,6,1.f,req?C_GOLD:C_BORDER);

        DrawText(nm,cX+12,ry+7,15,req?C_GOLD_L:C_TEXT);
        DrawText(TextFormat("you: %d",youVal),cX+220,ry+9,12,C_MUTED);

        // stepper:  [-]  value  [+]
        Rectangle minus={(float)(cX+G_W-150),(float)(ry+3),30.f,rowH-12};
        Rectangle plus ={(float)(cX+G_W-40 ),(float)(ry+3),30.f,rowH-12};
        if(SmallBtn(minus,"-",C_PANEL3,C_TEXT)){ *slot-=step; if(*slot<0)*slot=0; }
        if(SmallBtn(plus ,"+",C_PANEL3,C_TEXT)){ *slot+=step; if(*slot>maxVal)*slot=maxVal; }
        char vs[16]; snprintf(vs,sizeof(vs),"%d",*slot);
        DrawText(vs,cX+G_W-115+(70-MeasureText(vs,16))/2,ry+8,16,req?C_GOLD_L:C_MUTED);

        ry+=rowH;
    }
    EndScissorMode();

    // Create button
    if(Btn((Rectangle){(float)cX,(float)(G_BOTTOM-42),240.f,38.f},"Found Guild",C_GOLD,C_BG)){
        if(CommitNewGuild()){ ResetCreateBuffers(); guild_view=0; }
    }
    if(Btn((Rectangle){(float)(cX+256),(float)(G_BOTTOM-42),160.f,38.f},"Clear",C_PANEL3,C_TEXT))
        ResetCreateBuffers();
}

void GuildHandleEscape(void)
{
    if(guild_view==3 && tbGuildName.active) tbGuildName.active=false;
    else guild_view=0;
}

// ---- Guild tab dispatcher --------------------------------------------------

void DrawTabGuild(int cX, int cY, const int *attrs, const int *skills, const int *stats)
{
    switch(guild_view){
        case 1:  DrawGuildYours (cX,cY,attrs,skills,stats); break;
        case 2:  DrawGuildJoin  (cX,cY,attrs,skills,stats); break;
        case 3:  DrawGuildCreate(cX,cY,attrs,skills,stats); break;
        default: DrawGuildMenu   (cX,cY);                    break;
    }
}
