Oikion
A unified MLS, CRM, and team-intelligence platform for Greek real-estate agencies. Designed in Greek, from the entity model up.
Impact
Oikion replaced the patchwork of spreadsheets and WhatsApp groups that small Greek agencies, five to fifteen agents, had stitched together just to get through the week. When it went into pilot use, I stopped watching conversion numbers. The moment that mattered was quieter: an agent reaching for Oikion instead of the old spreadsheet, without really thinking about it, because for once the software worked the way their head already did.
How would you like to read this?
Same story, tailored depth. Choose the hat you are wearing and the case study expands what matters most to you.
Showing everything, with the deep architecture diagrams collapsed. Pick a lens and the case study expands what matters to you. This view itself never hides anything.
The problem I couldn’t stop thinking about
Oikion (pronounced ee-KEE-on, Greek for “dwelling”) started as a problem I couldn’t put down. Every industry has its software graveyard: tools built for a world that has since moved on, quietly broken, kept alive because replacing them feels scarier than limping along with them. Greek real-estate agencies were living in one of the worst I had ever seen.
I spent weeks embedded with agencies of five to fifteen agents across Athens and Thessaloniki, sitting beside people while they worked. What I found wasn’t a technology gap. It was a quieter mismatch, between the way the software pictured the world and the way agents actually moved through it. Their tools came in three flavours: generic international CRMs built for B2B sales pipelines, with no idea what a property transaction even is; legacy Greek desktop software from the late 1990s, installed on a single machine and visually frozen somewhere around Windows XP; and, most often, a homemade stack of spreadsheets, WhatsApp groups, and paper ledgers they had cobbled together themselves because nothing else fit.
None of them was the right tool. And yet the spreadsheet, of all things, was the honest one: it never pretended to be more than it was. That honesty is exactly why it had been so hard to replace.
That question became Oikion. Over roughly five months it grew from a cleaner CRM into four hard systems I had to design the human experience for: an engine that swallows the messy spreadsheets agencies already keep, a network that lets rival offices share without fear, a legal signing flow, and an encryption layer that makes the most sensitive data unreadable even to us. The rest of this study is really about those four, and about the one job the interface had in every case: to make something genuinely complex feel calm, safe, and obvious.
The field wasn’t empty. It was incoherent
Greek agencies were not short of software. They were short of software that agreed with itself. The serious incumbents are mature, capable products, and pretending otherwise would have been the fastest way to lose the argument.
ilistCRM, the CRM tied to the Spitogatos network, has run for about eighteen years and sits inside 850-plus agencies across Greece and Cyprus. RealStatus, out of Thessaloniki, has run nearly as long and serves 724. Both are ISO 27001 certified, both syndicate to two dozen-plus property portals, and both already cross-match properties against client requests. Below them sit the portals themselves (Spitogatos and xe.gr) for search, and the spreadsheet-plus-WhatsApp-plus-ledger stack that most small offices actually run the day on.
The shared tell is older than any of them: the word mandate. Treated as a listing the agency holds, it models the business supply-first. Greek agents mean the opposite: a brief a buyer hands you, a demand signal. The incumbents bolt request-matching onto a property-centric core. Oikion makes the Request a first-class, demand-side entity from the data model up. Same word, opposite centre of gravity.
| Criteria | ||||
|---|---|---|---|---|
| ilistCRM (Fortunet) | ~18 yrs · 850+ agencies | ~24 portals (Spitogatos network) | Property-centric + request matching | Accreted over 18 years |
| RealStatus (iarts) | ~18 yrs · 724 agencies | 28 portals + international | Property / request handling | Feature-rich, accreted |
| Oikion | Pre-launch challenger | Network-first; few portals yet | Demand-aware: Requests first-class | Designed from the model up |
So the incumbents were the competition, but the humble spreadsheet was the real benchmark, and it set the bar in four places the old tools kept missing. Agencies’ real data lived in messy bilingual spreadsheets that took days to hand-split and retype. A buyer with no match, or a listing with no buyer, had no safe way to travel across rival offices. Contracts were printed, signed by hand, scanned, and emailed, with sensitive party data along for the ride. And the most private financial and tax details were guarded by nothing sturdier than a promise. Each of those gaps became a system I had to design, not just a feature: an import engine, an inter-agency network called Polis, a self-hosted signing flow, and an end-to-end encryption layer. The rest of this study meets them one at a time.
The fastest way to lose a user is to ask for everything at once
Every legacy tool I audited opened with the same wall: one page, every field a Greek agency could ever need, all demanded before the product had earned a single keystroke of trust. Company name, VAT number, tax office, professional licence, two addresses, a phone for every occasion. It is the most honest expression of software that serves itself.
So I built both, side by side. Scroll the competitor wall on the left; step through Oikion’s real flow on the right.
// representative composite · scroll the wall, then submit
// Steps: 0=Language, 1=Welcome, 2=Theme, 3=Username,// 4=Notifications, 5=Security&Privacy, 6=Reviewconst TOTAL_STEPS = 7; // Calculate progress (excluding language selection step from progress display)const progressSteps = TOTAL_STEPS - 1;const displayStep = currentStep > 0 ? currentStep - 1 : 0;const progress = currentStep <= 1 ? 0 : ((displayStep) / (progressSteps - 1)) * 100; // Progress bar shows only from step 2 onward, because// language + welcome are zero-friction entry, not work.{currentStep >= 2 && <Progress value={progress} />}Oikion asks for three things to make an account (email, password, confirm), then gets out of the way. Everything else became a seven-step progressive flow, and the progress bar deliberately stays hidden until step three: choosing a language and saying hello are explicitly not work, and the interface refuses to dress them up as if they were.
“A system, not a skin”
Before any token, three words decided every visual argument. They gave me something to hold a decision against instead of debating taste, and each carried a behavioural rule, not just a mood.
Oikion was designed in Greek first and verified in English, because Greek runs about a third longer and every compact element has to survive the harder language. Underneath sits a token system: HSL semantic colours, a four-point spacing scale, fifteen hue families each with five variants, and OKLCH badge tokens where perceptual evenness matters.
The type scale is a fixed six-step ramp, set in Manrope (the primary interface face, with the legacy Inter being phased out) and chosen so hierarchy reads from weight and size alone, not from colour.
Light and dark are explicitly not one inverted into the other; they were designed as peers from the token layer up. Four themes ship today (Estate Light, Estate Dark, Twilight Lavender, and a System mode), with Pearl Sand designed and queued behind its token block.
The hardest decision in this layer was visibility. A property carries one of four states, on a single drag track from Hidden to Public.
Your organisation only; participates in intra-agency matchmaking.
Downgrading is consequential, so the guarantee lives where it cannot be skipped: in the database. Dropping a property below Secure deletes its cross-organisation match records atomically, inside a single Prisma transaction, and the UI updates optimistically and rolls back if the server refuses. A named are-you-sure confirmation is designed and queued next; today, Oikion is explicitly not relying on a confirmation dialog for that guarantee, in favour of the atomic transaction itself.
That slider is really the front door to Polis, the inter-agency network, and Polis turned out to be less a matching feature than a trust problem. Fiercely private, competing offices will only share data if they stay in control of it, so I gave sharing two dimensions instead of one. The slider decides whether a record is shared at all. A second, separate control, the one below, decides how much of it others see: Anonymised shows bare specifications, Agency-identified adds the office name and logo, and Full adds the listing agent and their contact. An agency can join the network revealing almost nothing, then open up to the partners it comes to trust.
The privacy tiers are resolved when a record is read, not baked in when it is written, so a change of heart takes effect the instant an agent makes it, with nothing to recompute and no stale copy left behind. The source agency’s current setting always wins, and a viewing office can never widen it. Matching was the reward. Control was the product.
Matching isn’t a filter, it’s a weighted score: nineteen criteria, budget weighted highest, with three factors that only make sense in Greece (Golden Visa eligibility, inside-the-city-plan status, and an energy class from A-plus to H) and weights an agency can retune to its own market.
| Criteria | ||
|---|---|---|
| Budget | 20 | The deal-breaker; graduated, not binary |
| Property type / Location | 12 each | Wrong type or area ends the conversation |
| Bedrooms / Size | 8 / 7 | Liveability, scored on a curve |
| Greek-specific (Golden Visa, city-plan, energy) | 2–3 each | Real constraints no foreign CRM models |
Invisible security: the whole thing behind one PIN
The most demanding design problem in Oikion is one you never see. Agencies hold financial positions, tax numbers, and private negotiations, and I wanted to offer a real guarantee instead of a comforting phrase: that Oikion itself cannot read the most sensitive collaborative data, full stop. The trouble is that true end-to-end encryption is famously cruel to users. Keys, lost devices, and recovery normally leak straight into the interface.
My brief for this one was a single sentence: make a real “we cannot read your data” guarantee feel as calm as a password field. The answer was one PIN. A single four-to-eight digit PIN, set once during onboarding, unlocks a person’s entire encrypted world across every organisation they belong to: comments on clients and properties, direct messages, group channels, and file attachments. Underneath, each record behaves like its own encrypted channel and access is a key shared to your identity, but none of that vocabulary ever reaches the user. They see a lock icon and a PIN.
The real work lived in the edge states. Recording recovery codes, the most user-hostile moment in any encrypted product, became a guided step during onboarding rather than a wall of warnings. During a PIN reset, while the system quietly re-shares access to the new key, the affected records read “Restoring access to encrypted comments…” while everything else stays usable, a degraded state I designed on purpose rather than leaving to a spinner. And I kept the one truth that cannot be designed away: if an organisation’s only admin loses both their PIN and every recovery code, the data is genuinely gone. The interface says so, plainly, at the moment it matters. An honest message beats a false reassurance.
Where the product fought back
Good design work isn’t a series of correct decisions. It’s decisions, mistakes, and recoveries, and the recoveries are where the durable insights live. Three of them mattered most.
1. The most expensive decision was delaying the right one
The original model treated buyer briefs as supply-side constructs attached to properties, the same error the legacy tools made. When testing proved it wrong, every one of the fifty-plus files built on it was wrong in the same way: API routes, server actions, schema, forms, validation, translations, search filters, permission guards. Correcting it meant a phased migration, with Mandates becoming Requests (demand-side constructs held by buyers) across multiple schema changes and deprecated endpoints.
2. The bug that wasn’t a bug
In the seven-step property wizard, Select values vanished when users navigated back, then reappeared going forward; occasionally a number typed into “size” surfaced in “bedrooms.” No JavaScript error. The form store held the values correctly. It only reproduced after repeated back-and-forth navigation. The cause spanned four systems that look like one until they disagree: React reused DOM nodes for same-shaped steps, the browser’s native number-input state bled across, react-hook-form’s registry silently dropped fields, and Zod’s cross-field .refine() ran during per-field validation against data that didn’t exist yet.
The fix, once diagnosed, was a single line: force a fresh mount per step so native DOM state can’t bleed, while the form store keeps the values:
// shouldUnregister:false keeps values in the form store,
// so entries survive the unmount, but the DOM is fresh,
// so browser-native state never bleeds between steps.
<CardContent key={currentStep}>
{renderStep(currentStep)}
</CardContent>3. Three voices speaking at once
At one point three toast implementations coexisted: sixty files importing Sonner directly with hardcoded strings, fifteen using the intended useAppToast hook backed by translation keys, and twenty-five on a deprecated Radix component with a different API. Different durations, different treatments, different languages, so Greek agents would occasionally see English feedback. Nobody added a third system maliciously; each developer picked the path of least resistance in the moment. The fix standardised on useAppToast (errors 6s, warnings 5s, success and info 4s, all routed through translation keys) and added ESLint rules, so the constraint is enforced at authoring time, not caught in review.
The same instinct resolved the quieter ones: 3,089 TypeScript errors that traced to a single dangling messages.d.ts reference; sub-44px touch targets fixed with pseudo-element hit areas; seventeen fixed-width panels that clipped on the 1366×768 Windows laptops Greek offices actually use; and a navigation includes() check that wasn’t the same as “is active.” Each looked like distributed breakage and turned out to be a single upstream cause.
The battles a layer down
The three problems above were fought at the interface. The next three were fought underneath it, where the screen sat on top of a hard distributed-systems or cryptography problem, and the design job was to keep every bit of that difficulty off the user’s desk.
4. Designing with the platform’s real limits: the Import Engine
The Import Engine takes one messy bilingual spreadsheet and turns it into clean, linked, encrypted records. A single row might hold a property, its owner’s phone, and a buyer’s budget all at once, so the engine has to work out what each row contains and create everything in the right order, atomically, so a failed import never leaves half-built data behind. That atomicity kept colliding with the production database’s real ceilings: it rejected the fast raw-SQL patterns and capped how long a transaction could run, right below what the bulk insert needed. The shape went back and forth several times before it settled into a two-pass algorithm: count the unique entities, generate all their IDs in one batched call, then build and insert in ordered phases inside a single transaction. It exists because of those limits, not in spite of them, and designing with a platform’s real ceilings instead of an imagined ideal is one of the lines between mid-level and senior work.
5. Two systems that can’t share a transaction: the signing flow
Document signing had to make a legally binding, multi-party, asynchronous process feel like one calm tab, across a boundary where the usual safety net does not exist. Oikion’s database and the external signing engine are two separate systems, and two separate systems cannot share a transaction. If the engine accepted a contract but the database then failed to record it, I would be left with a ghost envelope. So I ordered the steps on purpose: the external call happens first, before any database write. If the engine fails, there is no record and nothing to undo. If it succeeds but the database write then fails, the system fires a best-effort cancellation so no orphaned envelope lingers on the other side. The engine is self-hosted, a choice driven entirely by data sovereignty, because the alternative was shipping GDPR-sensitive party data to a processor overseas. The party data never leaves, and Oikion stays the single source of truth.
The interface hides all of it. An agent lines up signers in a three-step modal, with drag-to-reorder that announces itself to screen readers, sends the envelope, and watches its status climb through a view that is quietly polling the engine every fifteen seconds, because there is no push channel back to the browser. When the last person signs, a new document tagged Signed appears on its own, inheriting every link the original had. The distributed-systems complexity is real. The experience is a calm tab.
6. The edge case that could lock users out forever: E2EE session backup
The hardest single problem in the encryption system was a quiet one: what happens when a user clears their browser storage, switches to a new device, or opens a private window? In an end-to-end encrypted system, the keys that decrypt your history live only in your browser. Lose that local storage with no recovery design and the encrypted comments and messages are gone for good, and it also meant encryption could not follow a user across their own two devices at all.
So I designed encrypted session backup, and designed it in two layers. An inner layer encrypts the session data to the user’s own identity key, so the server can never read it. An outer layer wraps that again with a per-organisation key for encryption at rest. A server breach without the user’s key yields only opaque blobs; a stolen device without the server yields only wrapped data. I benchmarked the design openly against how Signal and WhatsApp handle encrypted backups, and the spec went through review and was revised seven separate times, each revision a documented trade-off: scoping backups per-organisation because a user’s keys differ across orgs; a best-effort save on unload that accepts the risk of losing a few seconds of state on a hard crash; and deliberately leaving one-time keys out of the backup, because carrying them across devices would let them be reused and break a core guarantee.
How the thing actually got built
A case study that only shows the polished surface is hiding the part that matters. Oikion was built as a loop (research, write, audit, revise, repeat), and the loop left evidence.
The Mandates-to-Requests rename is the clearest example. Getting the central noun wrong meant fifty files were wrong in the same way; correcting it meant a scripted migration and a rule I now keep: the four i18n registration points are one atomic unit, touched together or not at all.
There are no wireframes in this project, and that was deliberate. AI design-and-build tools have collapsed the distance between an idea and a working, themed screen, so the wireframe, whose whole value was being cheap to throw away, had lost most of its reason to exist.
But the bigger reason was the user. I was testing with working real-estate agents, not designers. A wireframe asks a non-technical person to imagine the product from grey boxes, and it reliably produced question marks; a finished screen produced opinions. Skipping the abstraction did not just save time. It improved the feedback, because people were reacting to the thing instead of a diagram of the thing.
What Oikion became
Oikion is a product built twice: once to understand what it should be, and once to become it. What emerged puts three modules on a single screen: the MLS listing inventory, the CRM for contacts and deal pipeline, and Oikosync, a real-time team activity feed carried over Ably that keeps a team live without polling.
Beneath those three modules sit the four hard systems the earlier chapters followed: the Import Engine that swallows an agency’s old spreadsheets, the Polis network that lets rival offices share on their own terms, the self-hosted signing flow, and the end-to-end encryption that keeps the most sensitive data unreadable even to us. None of them announces itself. That was the point.
The design system underneath isn’t a style guide. It’s a set of enforced constraints: ESLint catches hardcoded colours at authoring time, TypeScript enforces translation-key correctness at compile time, and components ship WCAG-compliant defaults so the accessible choice is the easy one. The data layer is multi-tenant by design: every query scoped to an organisation, sensitive fields encrypted per-org with a wrapped-key architecture, and a four-role system (Owner, Lead, Member, Viewer) enforced at the server-action layer before any data is touched.
Set against the incumbents I actually have to win against, the honest picture is mixed, and saying so is the whole point. They are ahead where eighteen years buys reach and features; Oikion is ahead where a coherent model and design discipline buy fit.
| Criteria | ||||
|---|---|---|---|---|
| ilistCRM (Fortunet) | 18 yrs, 850+ agencies, ~24 portals | AI integrating | Matching on a property core | Native, accreted |
| RealStatus (iarts) | 18 yrs, 724 agencies, 28 portals | ML, native apps, voice AI | Property + request handling | Native, feature-rich |
| Oikion | Pre-launch challenger | Focused core, no ML yet | Requests, first-class | Designed Greek-first, 3-field entry |
I’m proud of what Oikion is. I’m prouder of the decisions it required, especially the ones that hurt to make.
Building something that deserves this kind of care?
This is how I work: from the entity model to the last pixel, with the honest parts left in. If that is the sort of partner your team is looking for, I would love to hear from you.