diff --git a/app.rb b/app.rb
index b1fc481..a73725e 100644
--- a/app.rb
+++ b/app.rb
@@ -4,9 +4,7 @@ require 'yaml'
# Configure Sinatra
set :port, ENV['PORT'] || 4567
-set :bind, '0.0.0.0'
-set :public_folder, 'public'
-set :views, 'views'
+set :bind, '::'
# Load responses from YAML file
RESPONSES = YAML.load_file('responses.yml')
@@ -217,3 +215,244 @@ get '/*' do
}
end
end
+
+__END__
+
+@@index
+
+
+
+
+
+ No as a Service
+
+
+
+
+
No as a Service
+
The definitive multilingual API for negative responses
+
+
+ Currently in: <%= language_name %>
+ <% if language == 'no' %>🇳🇴 <% end %>
+ <% if language == 'sv' %>🇸🇪 <% end %>
+ <% if language == 'da' %>🇩🇰 <% end %>
+ <% if language == 'is' %>🇮🇸 <% end %>
+ <% if language == 'fi' %>🇫🇮 <% end %>
+ <% if language == 'fo' %>🇫🇴 <% end %>
+
+
+
+ <%= no_response %>
+
+
+
+
+ Get Another No
+
+
+
+ <% available_languages.each do |lang| %>
+ >
+ <%= lang[:name] %>
+
+ <% end %>
+
+
+
+
+
API Usage
+
JSON API: GET /api/no or GET /api/no/:lang
+
Languages: GET /languages
+
URL Parameter: ?lang=<%= language %>
+
Accept-Language header supported
+
Health Check: GET /health
+
+
+
+
+
+
diff --git a/public/css/styles.css b/public/css/styles.css
deleted file mode 100644
index 5e81881..0000000
--- a/public/css/styles.css
+++ /dev/null
@@ -1,342 +0,0 @@
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- min-height: 100vh;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
-}
-
-.container {
- text-align: center;
- max-width: 700px;
- padding: 2rem;
- background: rgba(255, 255, 255, 0.1);
- backdrop-filter: blur(10px);
- border-radius: 20px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
- border: 1px solid rgba(255, 255, 255, 0.2);
-}
-
-h1 {
- font-size: 3rem;
- margin-bottom: 1rem;
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
-}
-
-.subtitle {
- font-size: 1.2rem;
- margin-bottom: 1rem;
- opacity: 0.9;
-}
-
-.language-info {
- font-size: 1rem;
- margin-bottom: 2rem;
- padding: 0.5rem 1rem;
- background: rgba(255, 255, 255, 0.1);
- border-radius: 10px;
- display: inline-block;
-}
-
-.logo-container {
- position: relative;
- display: inline-block;
- margin-bottom: 2rem;
-}
-
-.logo-container::before {
- content: '';
- position: absolute;
- top: -20px;
- left: -20px;
- right: -20px;
- bottom: -20px;
- background: radial-gradient(
- circle at 50% 50%,
- transparent 60px,
- rgba(255, 107, 107, 0.1) 80px,
- transparent 120px
- );
- border-radius: 50%;
- animation: pulseRing 3s ease-in-out infinite;
- pointer-events: none;
-}
-
-.logo {
- width: 140px;
- height: 140px;
- background: radial-gradient(circle at 30% 30%, #ff6b6b, #ee5a52, #dc3545);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 5rem;
- text-shadow: 3px 3px 8px rgba(0, 0, 0, 0.6);
- box-shadow:
- 0 8px 32px rgba(255, 107, 107, 0.4),
- inset 0 2px 10px rgba(255, 255, 255, 0.3),
- inset 0 -2px 10px rgba(0, 0, 0, 0.2);
- border: 3px solid rgba(255, 255, 255, 0.4);
- position: relative;
- animation: logoFloat 4s ease-in-out infinite;
- cursor: pointer;
- transition: all 0.3s ease;
- overflow: hidden;
-}
-
-.logo::before {
- content: '';
- position: absolute;
- top: -5px;
- left: -5px;
- right: -5px;
- bottom: -5px;
- background: conic-gradient(
- from 0deg,
- #ff6b6b,
- #ff8e8e,
- #ffb3b3,
- #ff6b6b,
- #ee5a52,
- #ff6b6b
- );
- border-radius: 50%;
- z-index: -1;
- animation: rotate 4s linear infinite;
- opacity: 0.8;
-}
-
-.logo::after {
- content: '';
- position: absolute;
- top: 15%;
- left: 25%;
- width: 30px;
- height: 30px;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.8) 0%, transparent 70%);
- border-radius: 50%;
- filter: blur(8px);
- animation: shimmer 3s ease-in-out infinite alternate;
-}
-
-.logo:hover {
- transform: scale(1.1) rotate(5deg);
- box-shadow:
- 0 12px 40px rgba(255, 107, 107, 0.6),
- inset 0 2px 15px rgba(255, 255, 255, 0.4),
- inset 0 -2px 15px rgba(0, 0, 0, 0.3);
- animation-play-state: paused;
-}
-
-.logo:active {
- transform: scale(0.95);
- animation: logoShake 0.5s ease-in-out;
-}
-
-.no-response {
- font-size: 4rem;
- font-weight: bold;
- color: #ff6b6b;
- text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.5);
- margin: 2rem 0;
- padding: 1rem;
- background: rgba(255, 255, 255, 0.1);
- border-radius: 15px;
- border: 2px solid rgba(255, 107, 107, 0.3);
- word-break: break-word;
-}
-
-.controls {
- margin: 2rem 0;
- display: flex;
- flex-wrap: wrap;
- gap: 1rem;
- justify-content: center;
- align-items: center;
-}
-
-.refresh-btn {
- background: linear-gradient(45deg, #ff6b6b, #ff8e8e);
- color: white;
- border: none;
- padding: 1rem 2rem;
- font-size: 1.1rem;
- border-radius: 50px;
- cursor: pointer;
- transition: all 0.3s ease;
- box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
-}
-
-.refresh-btn:hover {
- transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4);
-}
-
-.language-selector {
- background: rgba(255, 255, 255, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.3);
- color: white;
- padding: 0.5rem 1rem;
- border-radius: 25px;
- font-size: 1rem;
- cursor: pointer;
-}
-
-.language-selector option {
- background: #444;
- color: white;
-}
-
-.api-info {
- margin-top: 2rem;
- padding: 1rem;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 10px;
- font-size: 0.9rem;
-}
-
-.api-info code {
- background: rgba(0, 0, 0, 0.2);
- padding: 0.2rem 0.5rem;
- border-radius: 4px;
- font-family: 'Courier New', monospace;
-}
-
-.nordic-flag {
- display: inline-block;
- margin-left: 0.5rem;
- font-size: 1.2em;
-}
-
-/* Animations */
-
-/* Floating animation */
-@keyframes logoFloat {
- 0%, 100% {
- transform: translateY(0px) rotate(0deg);
- }
- 25% {
- transform: translateY(-8px) rotate(1deg);
- }
- 50% {
- transform: translateY(-12px) rotate(0deg);
- }
- 75% {
- transform: translateY(-6px) rotate(-1deg);
- }
-}
-
-/* Enhanced rotation animation */
-@keyframes rotate {
- from {
- transform: rotate(0deg) scale(1);
- }
- 25% {
- transform: rotate(90deg) scale(1.02);
- }
- 50% {
- transform: rotate(180deg) scale(1);
- }
- 75% {
- transform: rotate(270deg) scale(1.02);
- }
- to {
- transform: rotate(360deg) scale(1);
- }
-}
-
-/* Shimmer effect for the highlight */
-@keyframes shimmer {
- 0% {
- opacity: 0.3;
- transform: scale(0.8);
- }
- 100% {
- opacity: 0.8;
- transform: scale(1.2);
- }
-}
-
-/* Shake animation on click */
-@keyframes logoShake {
- 0%, 100% { transform: translateX(0); }
- 10% { transform: translateX(-5px) rotate(-2deg); }
- 20% { transform: translateX(5px) rotate(2deg); }
- 30% { transform: translateX(-5px) rotate(-1deg); }
- 40% { transform: translateX(5px) rotate(1deg); }
- 50% { transform: translateX(-3px); }
- 60% { transform: translateX(3px); }
- 70% { transform: translateX(-2px); }
- 80% { transform: translateX(2px); }
- 90% { transform: translateX(-1px); }
-}
-
-/* Pulsing ring animation */
-@keyframes pulseRing {
- 0%, 100% {
- transform: scale(0.8);
- opacity: 0.3;
- }
- 50% {
- transform: scale(1.1);
- opacity: 0.1;
- }
-}
-
-/* Sparkle animation */
-@keyframes sparkleFloat {
- 0% {
- opacity: 1;
- transform: translateY(0) scale(0);
- }
- 50% {
- opacity: 1;
- transform: translateY(-30px) scale(1);
- }
- 100% {
- opacity: 0;
- transform: translateY(-60px) scale(0) rotate(180deg);
- }
-}
-
-/* Responsive Design */
-@media (max-width: 600px) {
- .container {
- margin: 1rem;
- padding: 1.5rem;
- }
-
- h1 {
- font-size: 2rem;
- }
-
- .logo {
- width: 100px;
- height: 100px;
- font-size: 3.5rem;
- }
-
- .logo::after {
- width: 20px;
- height: 20px;
- top: 20%;
- left: 30%;
- }
-
- .no-response {
- font-size: 2.5rem;
- }
-
- .controls {
- flex-direction: column;
- gap: 0.5rem;
- }
-}
diff --git a/public/js/scripts.js b/public/js/scripts.js
deleted file mode 100644
index b0c0e57..0000000
--- a/public/js/scripts.js
+++ /dev/null
@@ -1,109 +0,0 @@
-// Language selector functionality
-function changeLanguage(lang) {
- const url = new URL(window.location);
- url.searchParams.set('lang', lang);
- window.location.href = url.toString();
-}
-
-// Enhanced logo interaction
-function logoClick() {
- // Add a fun interaction when logo is clicked
- const logo = document.querySelector('.logo');
- logo.style.animationPlayState = 'running';
-
- // Get a new "no" response when logo is clicked
- setTimeout(() => {
- location.reload();
- }, 500);
-}
-
-// Sparkle effects on hover
-function createSparkles(element) {
- const rect = element.getBoundingClientRect();
- const sparkles = 5;
-
- for (let i = 0; i < sparkles; i++) {
- const sparkle = document.createElement('div');
- sparkle.innerHTML = '✨';
- sparkle.style.position = 'fixed';
- sparkle.style.left = (rect.left + Math.random() * rect.width) + 'px';
- sparkle.style.top = (rect.top + Math.random() * rect.height) + 'px';
- sparkle.style.fontSize = (Math.random() * 20 + 10) + 'px';
- sparkle.style.pointerEvents = 'none';
- sparkle.style.zIndex = '1000';
- sparkle.style.animation = 'sparkleFloat 2s ease-out forwards';
-
- document.body.appendChild(sparkle);
-
- setTimeout(() => {
- sparkle.remove();
- }, 2000);
- }
-}
-
-// Initialize event listeners when DOM is loaded
-document.addEventListener('DOMContentLoaded', function() {
- // Add sparkle effects on logo hover
- const logo = document.querySelector('.logo');
- if (logo) {
- logo.addEventListener('mouseenter', function() {
- createSparkles(this);
- });
- }
-
- // Add keyboard shortcuts
- document.addEventListener('keydown', function(event) {
- // Press 'R' to refresh and get new response
- if (event.key.toLowerCase() === 'r' && !event.ctrlKey && !event.metaKey) {
- location.reload();
- }
-
- // Press 'L' to focus language selector
- if (event.key.toLowerCase() === 'l' && !event.ctrlKey && !event.metaKey) {
- const languageSelector = document.querySelector('.language-selector');
- if (languageSelector) {
- languageSelector.focus();
- }
- }
- });
-
- // Add subtle animations to elements on scroll (if needed in future)
- const observer = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- entry.target.style.opacity = '1';
- entry.target.style.transform = 'translateY(0)';
- }
- });
- });
-
- // Observe elements for animation (currently not needed but prepared for future use)
- const animatedElements = document.querySelectorAll('.container > *');
- animatedElements.forEach(el => {
- observer.observe(el);
- });
-});
-
-// Add some fun easter eggs
-let clickCount = 0;
-const logo = document.querySelector('.logo');
-
-if (logo) {
- logo.addEventListener('click', function() {
- clickCount++;
-
- // Easter egg: Multiple rapid clicks
- if (clickCount >= 5) {
- this.style.animation = 'logoShake 0.5s ease-in-out, rotate 2s linear infinite';
- setTimeout(() => {
- this.style.animation = 'logoFloat 4s ease-in-out infinite';
- }, 2000);
- clickCount = 0;
- }
- });
-}
-
-// Reset click count after a delay
-setInterval(() => {
- clickCount = 0;
-}, 3000);
diff --git a/views/index.erb b/views/index.erb
deleted file mode 100644
index 7ddb0f0..0000000
--- a/views/index.erb
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
-
-
- No as a Service
-
-
-
-
-
No as a Service
-
The definitive multilingual API for negative responses
-
-
- Currently in: <%= language_name %>
- <% if language == 'no' %>🇳🇴 <% end %>
- <% if language == 'sv' %>🇸🇪 <% end %>
- <% if language == 'da' %>🇩🇰 <% end %>
- <% if language == 'is' %>🇮🇸 <% end %>
- <% if language == 'fi' %>🇫🇮 <% end %>
- <% if language == 'fo' %>🇫🇴 <% end %>
-
-
-
-
-
- <%= no_response %>
-
-
-
-
- Get Another No
-
-
-
- <% available_languages.each do |lang| %>
- >
- <%= lang[:name] %>
-
- <% end %>
-
-
-
-
-
API Usage
-
JSON API: GET /api/no or GET /api/no/:lang
-
Languages: GET /languages
-
URL Parameter: ?lang=<%= language %>
-
Accept-Language header supported
-
Health Check: GET /health
-
-
-
-
-
-