I some minor things, I will not go into the structure but more about the nomenclature of it.
why upper case ?
if you're on an case insensitive environment like windows it does not matter anyhow and if not it's just annoying writing CAPS ALL THE TIME. Plus you loose the easy visual distinction between what is SQL and what are the fields etc
SELECT * FROM my_table WHERE ....
stay implicit in the direct context (this can be arguable but we can create aliases in queries)
CREATE TABLE `my_table` (
id int unsigned auto_increment,
....
CREATE TABLE `my_reference_table` (
my_table_id int unsigned
....
FOREIGN KEY (my_table_id) REFERENCES `my_table` (`id`)
....
and in queries
SELECT * FROM `my_table` mt INNER JOIN `my_reference_table` mrt ON mt.id = mrt.my_table_id ...
This is about philosophy I know a lot of database guys do the explicit every id is unique thing. I don't see it as a necessity also namespacing all fields all the time does not always make sense although you avoid involuntary column collisions ... it makes it harder to screw up but it lets you write way more code.
It's a question of taste.
another thing would be for created and modified just use 'created_at' and 'modified_at' so it's clear what they mean but it's less to write.
I personally hate fname just write first_name you already being overly expressive and it's way better to read.
Some normal forms are missing but as soon as you hit 1+ million you usually have to break some normalizations.
also check if the datetimes use a BTREE index (usually they do) and if you do explicit hits on names in search indexes try to use a HASH index since you don't need range search patterns.
I don't know about your login system if you use the username everything is fine but if you want to switch to emails at some later point the email should be unique.
those are my initial thoughts, they are all highly debatable I guess.
j
stuff ;)